/*
    Fighter.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 Fighter

+ new
{
  self = [super new];
  enemies = [OrdCltn new];
  stamina = maxstamina = 30.0;
  heartbeat_kill = panic = 0;
  ftype = warmonger;
  strcpy (deadname, "corpse");
  strcpy (deaddef, "the corpse");
  strcpy (deadindef, "a corpse");
  strcpy (deaddesc, "This is some anonymous dead thing.");
  return self;
}

- free
{
  /*  1996-04-15  New libobjects demands that I use dealloc instead */
  /*  of free. */
  /* NSDeallocateObject (enemies); */
  [enemies free]; 
  return [super free];
}

- kill: who: dobj
{
  if ([dobj isdead])
    return self;

  /*  We have to prevent people from getting in extra hits by typing kill */
  /*  over and over.  We only really want to hit if this function was called */
  /*  by the heartbeat.  The heartbeat is nice enough to set a flag for us, */
  /*  since otherwise it would be hard to tell. */

  if ([who checkkillflag]) {
    if (dobj == who) {
      [who clue: who];
      return self;
    }
    if (++beatcount == 4) {
      beatcount = 0;
      [[who getlocation] emote: who: "attack": "attacks": dobj: ""];
      [dobj hit: who: (float) (random() % 3)];
      [location theres_a_fight_going_on];
    }
  } else {
    if (dobj == who) {
      [who echo: "If you want to delete your account, e-mail the mud admin!"];
      [who clue: who];
      return self;
    }
    [[who enemies] addIfAbsent: dobj];
  }

  return self;
}

- (float) priority: (char *) action: (int) numargs
{
  if (numargs == 2) {
    if (!strcmp (action, "kill"))
      return 2.0;
  }
  /* Not supported */
  return -1.0;
}

- hit: fromwho: (float) damage
{
  if (dead)
    return self;
  [enemies addIfAbsent: fromwho];

  /*  Figure in level differences and armor. */
  if ([fromwho level] == -1)
    damage = damage + 20.0;   /* Mud admin always gets a big advantage */
  else {
    int i,m;
    float ac = 0.0;
    damage = damage + (float)([fromwho level]) - (float)([self level]);
    for(i=0,m=[contents size];i<m;i++) {
      id whatever = [contents at:i];
      /*  Armor is not cumulative! */
      if ([whatever isKindOf: [Armor class]])
        if ([whatever protection] > ac)
          ac = [whatever protection];
    }
    damage -= ac;
  }

  if (damage <= 0.0) {
    [location emote: self: "suffer": "suffers": " no damage"];
    if (stamina <= panic)
      [self runaway];
  } else if (stamina > damage) {
    float beforehealth, afterhealth;
    beforehealth = [self health];
    stamina -= damage;
    afterhealth = [self health];
    if (afterhealth <= 0.33 && beforehealth > 0.33)
      [location emote: self: "are": "is": " on the brink of death"];
    else if (afterhealth <= 0.66 && beforehealth > 0.66)
      [location emote: self: "are": "is": " hurting real bad"];
    else if (damage <= 2.0)
      [location emote: self: "are": "is": " hurt"];
    else if (damage <= 7.0)
      [location emote: self: "are": "is": " injured"];
    else if (damage <= 15.0)
      [location emote: self: "are": "is": " wounded"];
    else
      [location emote: self: "are": "is": " seriously wounded"];
    if (stamina <= panic)
      [self runaway];
  } else {
    int templevel;
    float tempstamina;
    if ([self isKindOf: [Player class]] &&
        [fromwho isKindOf: [Player class]] ) {
      char temp[80];
      sprintf (temp, "PK:  %s killed %s", [fromwho mudname], [self mudname]);
      cheezlog (temp);
    }
    /*  Make the gain-a-level message come _after_ the dies message. */
    templevel = [self level];
    tempstamina = [self maxstamina];
    [self die];
    if (templevel)
      [fromwho get_experience:
        (unsigned long) (tempstamina * (templevel + 1)) >> 1];
    else
      [fromwho get_experience: (unsigned long) tempstamina];
  }
  return self;
}

- runaway
{
  int result=0, choice, loop;
  for (loop=0;loop<12;loop++) {
    choice = random() % 6;
    switch (choice) {
    case 0:
      result = [self act: "n"];
      break;
    case 1:
      result = [self act: "s"];
      break;
    case 2:
      result = [self act: "e"];
      break;
    case 3:
      result = [self act: "w"];
      break;
    case 4:
      result = [self act: "u"];
      break;
    case 5:
      result = [self act: "d"];
      break;
    default:
      assert (0);
    }
    if (result)
      return self;
  }
  return self;
}

- die
{
  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];
  c = [[[Corpse new] describe: deadname: deadindef: deaddef: deaddesc]
    setlocation: location];
  [contents elementsPerform: @selector(setlocation:) with: c];
  [self logout];
  return self;
}

/*  This does the killing on each heartbeat. */
- killagain
{
  id victim = NULL;
  while (![enemies isEmpty]) {
    victim = get_random_member (enemies);
    if ([contents includes: victim])
      if (![victim isdead])
        break;
    if ([[location contents] includes: victim])
      if (![victim isdead])
        break;
    [self clue: victim];
    victim = NULL;
  }
  if (victim) {
    id t;
    if ((t = [self resolve_action: "kill": 2])) {
      heartbeat_kill = 1;
      [t kill: self: victim];
    } else
      assert (0);
  }
  return self;
}

- (void) heartbeat
{
  /*  Beatcount is managed in kill. */
  if (dead)
    return;
  if (stamina < [self maxstamina])
    stamina *= 1.001;
  if (stamina > [self maxstamina])
    stamina = [self maxstamina];
  [self killagain];
}

- clue: dontkillme
{
  while ([enemies find:dontkillme]) {  
    [enemies remove:dontkillme];
  }
  return self;
}

- unclue: killme
{
  [enemies addIfAbsent: killme];
  return self;
}

- (int) checkkillflag
{
  int t;
  t = heartbeat_kill;
  heartbeat_kill = 0;
  return t;
}

- theres_a_fight_going_on
{
  id c;
  int i,m;

  if (dead)
    return self;
  if (ftype == bystander || (![enemies isEmpty]))
    return self;
  if (ftype == pacifist)
    return [self runaway];

  /*  Kill 'em all! */

  c = [location contents];
  for(i=0,m=[c size];i<m;i++) {
    id whatever = [c at:i];
    if (whatever == self)
      continue;
    if ([whatever isKindOf: [Fighter class]] &&
        (![whatever isKindOf: [Player class]])) {
      int j,mm=[[whatever enemies] size];
      for(j=0;j<mm;j++) {
	id w2 = [[whatever enemies] at:j];
	[enemies addIfAbsent:w2];
      }
    }
  }

  return self;
}

- enemies
{
  return enemies;
}

- empty: who
{
  char temp[80];
  sprintf (temp, "%s is not carrying anything.", capitalize ([self def]));
  [who echo: temp];
  return self;
}

- nonempty: who
{
  char temp[80];
  sprintf (temp, "%s is carrying:", capitalize ([self def]));
  [who echo: temp];
  return self;
}

- (float) health
{
  return stamina / [self maxstamina];
}

- (float) stamina
{
  return stamina;
}

- (float) maxstamina
{
  return maxstamina;
}

- (void) heal
{
  stamina = [self maxstamina];
}

- get_experience: (unsigned long) exp
{
  return self;
}

@end