/*
    Player.m  Class definition.
    Copyright (C) 1995  David Flater.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cheezmud.h"

@implementation Player

+ new
{
  self = [super new];
  ftype = bystander;
  tty = NULL;
  return self;
}

- (void *) getty
{
  return tty;
}

- setty: (void *) term
{
  tty = term;
  return self;
}

- load
{
  char temp[80];
  FILE *fp;
  sprintf (temp, "db/%s", [self mudname]);
  assert (fp = fopen (temp, "r"));
  fgets (temp, 80, fp);
  [self setdef: noeol (temp)];
  [self setindef: [self def]];
  fgets (temp, 80, fp);
  [self setlongdesc: noeol (temp)];
  fgets (temp, 80, fp);
  [self teleport: global_find (noeol (temp), 1)];
  fscanf (fp, "%f %lu", &stamina, &experience);
  fclose (fp);
  return self;
}

- save
{
  char temp[80];
  FILE *fp;
  sprintf (temp, "db/%s", [self mudname]);
  assert (fp = fopen (temp, "w"));
  fprintf (fp, "%s\n", def);
  fprintf (fp, "%s\n", longdesc);
  if (location)
    fprintf (fp, "%s\n", [location mudname]);
  else
    fprintf (fp, "start\n");
  fprintf (fp, "%f %lu\n", stamina, experience);
  fclose (fp);
  return self;
}

- (int) level
{
  if (experience < 100)
    return 1;
  return 1 + (int)(log((double)experience / 100.0 * M_E));
}

- (float) priority: (char *) action: (int) numargs
{
  if (numargs == 1) {
    if (!strcmp (action, "l"))
      return 10.0;
    if (!strcmp (action, "names"))
      return 10.0;
    if (!strcmp (action, "inv"))
      return 10.0;
    if (!strcmp (action, "who"))
      return 10.0;
    if (!strcmp (action, "status"))
      return 10.0;
    if (!strcmp (action, "smile"))
      return 10.0;
    if (!strcmp (action, "frown"))
      return 10.0;
    if (!strcmp (action, "giggle"))
      return 10.0;
    if (!strcmp (action, "grumble"))
      return 10.0;
    if (!strcmp (action, "laugh"))
      return 10.0;
    if (!strcmp (action, "yawn"))
      return 10.0;
    if (!strcmp (action, "cough"))
      return 10.0;
    if (!strcmp (action, "spit"))
      return 10.0;
    if (!strcmp (action, "ver"))
      return 10.0;
  }
  if (numargs == 2) {
    if (!strcmp (action, "examine"))
      return 10.0;
    if (!strcmp (action, "get"))
      return 10.0;
    if (!strcmp (action, "drop"))
      return 10.0;
    if (!strcmp (action, "unbag"))
      return 10.0;
    if (!strcmp (action, "read"))
      return 10.0;
    if (!strcmp (action, "plunder"))
      return 10.0;
    if (!strcmp (action, "hug"))
      return 10.0;
    if (!strcmp (action, "finger"))
      return 10.0;
    if (!strcmp (action, "kiss"))
      return 10.0;
  }
  return [super priority: action: numargs];
}

- echo: (char *) text
{
  text_sock_write (tty, text);
  text_sock_write (tty, "\n");

  if (audit) {
    id ma;
    if (!(ma = global_find (mudadmin, 1))) {
      [self toggle_audit];
      return self;
    }
    text_sock_write ([ma getty], def);
    text_sock_write ([ma getty], " echo:  ");
    text_sock_write ([ma getty], text);
    text_sock_write ([ma getty], "\n");
  }

  return self;
}

/*  We'll get these from objects in our inventory. */
- emote: emoter: (char *) verb_you: (char *) verb_he: (char *) emotion
{
  char temp[160];
  sprintf (temp, "%s that you are carrying %s%s.", capitalize ([emoter def]),
    verb_he, emotion);
  return [self echo: temp];
}

- emote: emoter: (char *) verb_you: (char *) verb_he: dobj: (char *) emotion
{
  char temp[160], a2[80];
  if (dobj == self)
    strcpy (a2, "you");
  else
    strcpy (a2, [dobj def]);
  sprintf (temp, "%s that you are carrying %s %s%s.",
    capitalize ([emoter def]), verb_he, a2, emotion);
  return [self echo: temp];
}

- say: emoter: (char *) verb_you: (char *) verb_he: (char *) emotion
{
  char temp[160];
  sprintf (temp, "%s that you are carrying %s%s", capitalize ([emoter def]),
    verb_he, emotion);
  return [self echo: temp];
}

- smile: who
{
  [[who getlocation] emote: who: "smile": "smiles": ""];
  return self;
}

- frown: who
{
  [[who getlocation] emote: who: "look": "looks": " unhappy"];
  return self;
}

- giggle: who
{
  [[who getlocation] emote: who: "giggle": "giggles": ""];
  return self;
}

- grumble: who
{
  [[who getlocation] emote: who: "grumble": "grumbles": ""];
  return self;
}

- laugh: who
{
  [[who getlocation] emote: who: "laugh": "laughs": ""];
  return self;
}

- yawn: who
{
  [[who getlocation] emote: who: "yawn": "yawns": ""];
  return self;
}

- cough: who
{
  [[who getlocation] emote: who: "cough": "coughs": ""];
  return self;
}

- spit: who
{
  [[who getlocation] emote: who: "spit": "spits": ""];
  return self;
}

- ver: who
{
  [who echo: version];
  return self;
}

- hug: who: dobj
{
  [[who getlocation] emote: who: "hug": "hugs": dobj: ""];
  return self;
}

- kiss: who: dobj
{
  [[who getlocation] emote: who: "kiss": "kisses": dobj: ""];
  return self;
}

- finger: who: dobj
{
  [[who getlocation] emote: who: "make a rude gesture at":
    "makes a rude gesture at": dobj: ""];
  return self;
}

/*  This contains code duplicated from Fighter.m.  Eventually I may clean */
/*  this up, but for now it's easier this way. */
- die
{
  char temp[80];
  id c;
  dead = 1;
  [location emote: self: "die": "dies": ""];
  /*  Kluge:  theres_a_fight_going_on doesn't work if you get killed in */
  /*  one swipe, since you are not there for your buddies to query on who */
  /*  just killed you.  This works around that by calling it special just */
  /*  before you die. */
  [location theres_a_fight_going_on];
  sprintf (temp, "%s's corpse", indef);
  c = [[[Corpse new] describe: "corpse": temp: temp:
    "This is a player who has gotten killed."] setlocation: location];
  [contents elementsPerform: @selector(setlocation:) with: c];

  /*  Penalties for dying */
  experience >>= 1;
  [self setlocation: global_find ("start", 1)];
  stamina = 1.0;

  [self logout];
  return self;
}

- l: who
{
  if (![who getlocation]) {
    [who echo:
"This place is without form, and void.  You cannot see or hear or feel\n\
anything, and you are no longer sure if you even exist."];
    return self;
  }
  [[who getlocation] emote: who: "look": "looks": " around"];
  [who echo: capitalize ([[who getlocation] indef])];
  [who echo: [[who getlocation] longdesc]];
  [[who getlocation] listcontents: who];
  return self;
}

- names: who
{
  id c;
  int i,n;

  if ([who getlocation]) {
    [who echo: "Things in the room:"];
    c = [[who getlocation] contents];
    for(i=0,n=[c size];i<n;i++) {
      id whatever = [c at:i];
      text_sock_write ([who getty], [whatever mudname]);
      text_sock_write (tty, "\t");
    }
    text_sock_write ([who getty], "\n");
  }

  c = [who contents];
  if ([c isEmpty])
    return self;

  [who echo: "Things you are carrying:"];

  for(i=0,n=[c size];i<n;i++) {
    id whatever = [c at:i];
    text_sock_write ([who getty], [whatever mudname]);
    text_sock_write (tty, "\t");
  }
  text_sock_write ([who getty], "\n");
  return self;
}

- examine: who: dobj
{
  [[who getlocation] emote: who: "examine": "examines": dobj: ""];
  [who echo: [dobj longdesc]];
  [dobj listcontents: who];
  if ([dobj isKindOf: [Fighter class]]) {
    char temp[80];
    float health = [dobj health];
    if (health <= 0.33)
      sprintf (temp, "%s is on the brink of death.", capitalize ([dobj def]));
    else if (health <= 0.66)
      sprintf (temp, "%s is hurting real bad.", capitalize ([dobj def]));
    else if (health < 1.0)
      sprintf (temp, "%s is kinda beat up.", capitalize ([dobj def]));
    else
      sprintf (temp, "%s is uninjured.", capitalize ([dobj def]));
    [who echo: temp];
  }
  return self;
}

- read: who: dobj
{
  [dobj getread: who];
  return self;
}

- plunder: who: dobj
{
  if (![dobj isKindOf: [Corpse class]]) {
    [who echo: "You can only plunder corpses."];
    return self;
  }
  [[who getlocation] emote: who: "shake down": "shakes down": dobj: ""];
  [[dobj contents] elementsPerform: @selector(setlocation:)
    with: [who getlocation]];
  return self;
}

/*  Parse the command line. */
- (int) ohce: (char *) text
{
  if (audit) {
    id ma;
    if (!(ma = global_find (mudadmin, 1))) {
      [self toggle_audit];
    } else {
      text_sock_write ([ma getty], def);
      text_sock_write ([ma getty], " input:  ");
      text_sock_write ([ma getty], text);
      text_sock_write ([ma getty], "\n");
    }
  }

  if (!strcmp (text, "logout")) {
    [self logout];
    return 1;
  }
  if (!strcmp (text, "help")) {
    [self help];
    return 1;
  }
  if (!strncmp (text, "describe", 8)) {
    if (strlen (text) < 10)
      [self setlongdesc: "This player has a null description."];
    else
      [self setlongdesc: text+9];
    return 1;
  }
  if (!strncmp (text, "say", 3)) {
    if (strlen (text) < 5)
      [self echo: "Eh?"];
    else
      [location say: self: "say:  ": "says:  ": text+4];
    return 1;
  }
  if (!strncmp (text, "xmit", 4)) {
    id dobj;
    if (!(dobj = [self find: "radio": 1]))
      return 0;
    if (strlen (text) < 6)
      [self echo: "Eh?"];
    else {
      [[self getlocation] say: self: "say to the radio:  ":
        "says to the radio:  ": text+5];
      [dobj xmit: self: text+5];
    }
    return 1;
  }
  if (!strncmp (text, "password", 8)) {
    if (strlen (text) < 10)
      [self echo: "Password not changed."];
    else {
      set_password (mudname, text+9);
      [self echo: "Password changed."];
    }
    return 1;
  }

  /*  Actions */
  return [self act: text];
}

- who: who
{
  show_users ([who getty]);
  return self;
}

- logout
{
  if (!loggedout) {
    de_notify (tty);
    /* Quietly drop all possessions */
    [contents elementsPerform: @selector(setlocation:) with: location];
    [self save];
    [super logout];
    /*  Do this last in case there are more messages */
    delmark (tty);
  }
  return self;
}

- help
{
  [self echo: "Commands:"];
  [self echo: "  logout -- leave the mud"];
  [self echo: "  l -- (look) look around"];
  [self echo: "  names -- show names of objects in the room and your inventory"];
  [self echo: "  who -- show Cheezmud users"];
  [self echo: "  inv -- show inventory (what you are carrying)"];
  [self echo: "  n,s,e,w,u,d -- Move north/south/east/west/up/down"];
  [self echo: "  get name -- pick up something"];
  [self echo: "  take name -- same as get"];
  [self echo: "  drop name -- drop something"];
  [self echo: "  bag name -- put something you are carrying in a sack"];
  [self echo: "  unbag name -- take it out of a sack"];
  [self echo: "  read name -- read something"];
  [self echo: "  kill name -- kill something"];
  [self echo: "  plunder name -- steal from the dead"];
  [self echo: "  describe blah blah.... -- change your description"];
  [self echo: "  password blah blah.... -- change your password"];
  [self echo: "  say blah blah.... -- speak"];
  [self echo: "  status -- check your health, etc."];
  [self echo: "  smile, frown, giggle, grumble, laugh, yawn, cough, spit -- look lively"];
  [self echo: "  hug name -- hug somebody"];
  [self echo: "  kiss name -- kiss somebody"];
  [self echo: "  finger name -- make a rude gesture at somebody"];
  [self echo: "  ver -- Show cheezmud version"];
  [self echo: "To get the second sword, say \"get sword 2\"."];
  [self echo: "(That works for any action and for all values of n)"];
  [self echo: "Objects that you carry may give you additional commands;"];
  [self echo: "examine them to find out how to use them."];
  [self echo: "Please remember that long lines will be truncated!"];
  [self echo: "Everything is case-sensitive.  Most commands are lowercase."];
  [self echo: "Type 'names' if you can't figure out how to reference something."];
  [self echo: "Armor is not cumulative!"];
  return self;
}

- status: who
{
  char temp[80];
  sprintf (temp, "Your current stamina is %f", [who stamina]);
  [who echo: temp];
  sprintf (temp, "Your experience is %lu", [who experience]);
  [who echo: temp];
  return self;
}

- inv: who
{
  id c;
  int i,n;

  c = [who contents];
  if ([c isEmpty])
    [who echo: "You are not carrying anything."];
  else {
    [who echo: "You are carrying:"];
    for(i=0,n=[c size];i<n;i++) {
      id whatever = [c at:i];
      [who echo: capitalize ([whatever indef])];
    }
  }
  return self;
}

- get: who: dobj
{
  if ([[who contents] size] >= [who capacity]) {
    [who echo: "You can't carry any more."];
    return self;
  }

  /*  Can't pick up anything that will fight you. */
  if ([dobj isKindOf: [Fighter class]]) {
    [who echo: "Fat chance."];
    return self;
  }

  /*  Special */
  if ([dobj isKindOf: [Bee class]]) {
    [who echo: "That would be really stupid."];
    return self;
  }

  [[who getlocation] emote: who: "get ": "gets ": [dobj def]];
  [dobj setlocation: who];
  return self;
}

- unbag: who: dobj
{
  if ([[who contents] size] >= [who capacity]) {
    [who echo: "You can't carry any more."];
    return self;
  }

  /*  In case we somehow stuffed a Tasmanian Devil into a sack.... */
  if ([dobj isKindOf: [Fighter class]]) {
    [who echo: "Fat chance."];
    return self;
  }

  [[who getlocation] emote: who: "unbag ": "unbags ": [dobj def]];
  [dobj setlocation: who];
  return self;
}

- drop: who: dobj
{
  [[who getlocation] emote: who: "drop ": "drops ": [dobj def]];
  [dobj setlocation: [who getlocation]];
  return self;
}

- get_experience: (unsigned long) exp
{
  int l;
  l = [self level];
  if (experience < 4000000000)
    experience += exp;
  if (experience > 4000000000)
    experience = 4000000000;
  if ([self level] - l > 1)
    [self echo:  "Congratulations, you just gained *MULTIPLE* levels!"];
  else if ([self level] > l)
    [self echo:  "Congratulations, you just gained a level."];
  return self;
}

- (unsigned long) experience
{
  return experience;
}

- (float) maxstamina
{
  return [self level] * 15.0;
}

@end