/**************************************************************
 * FFTacticsMUD : projectile.cpp                              *
 **************************************************************
 * (c) 2002 Damien Dailidenas (Trenton). All rights reserved. *
 **************************************************************/

#include "main.h"
#include <strstream>

PROJECTILE *projectile_list;

PROJECTILE::PROJECTILE() {
  next = NULL;
  next_in_room = NULL;
  area = NULL;
  room = NULL;
  ch = NULL;
  target = NULL;
  onHit = NULL;
  busy = false;
  return;
}

PROJECTILE::~PROJECTILE() {
  from_room();

  if(this == projectile_list)
    projectile_list = next;
  else {
    for(PROJECTILE *prev = projectile_list; prev; prev = prev->next) {
      if(prev->next == this) {   
        prev->next = next;
        break;
      }
    }
  }

  return;
}

void PROJECTILE::to(ROOM *room) {
  if(area && room->area != area) {
    PROJECTILE *projectile_this = this;
    zap(projectile_this);
    return;
  }

  if(this->room)
    from_room();

  this->room = room;
  area = room->area;
  next_in_room = room->projectile;
  room->projectile = this;
  //check_collision();
  return;
}
 
void PROJECTILE::from_room() {
  if(!room)
    return;

  if(this == room->projectile)
    room->projectile = next_in_room;
  else {
    for(PROJECTILE *prev = room->projectile; prev; prev = prev->next_in_room) {
      if(prev->next_in_room == this) {
        prev->next_in_room = next_in_room;
        break;
      }
    }
  }

  room = NULL;
  area = NULL;
  return;
}

void PROJECTILE::check_collision() {
  PROJECTILE *projectile = room->get_moving_projectile(this);
  PROJECTILE *projectile_this = this;

  if(projectile) {
    becho(ch->name + "'s and " + projectile->ch->name + "'s energy projectiles collide!\n\r", ch, projectile->ch);
    ch->printf("Your energy projectile collides with " + projectile->ch->name + "'s!\n\r");
    projectile->ch->printf("Your energy projectile collides with " + ch->name + "'s!\n\r");
 
    if(projectile->dam > dam * 1.25) {
      becho(projectile->ch->name + "'s energy projectile overpowers " + ch->name + "'s!\n\r", ch, projectile->ch);
      ch->printf(projectile->ch->name + "'s energy projectile overpowers yours!\n\r");
      projectile->ch->printf("Your energy projectile overpowers " + ch->name + "'s!\n\r");
      zap(projectile_this);
    }
    else if(projectile->dam < dam * .75) {
      becho(ch->name + "'s energy projectile overpowers " + projectile->ch->name + "'s!\n\r", ch, projectile->ch);
      projectile->ch->printf(ch->name + "'s energy projectile overpowers yours!\n\r");
      ch->printf("Your energy projectile overpowers " + projectile->ch->name + "'s!\n\r");
      zap(projectile);
    }
    else {
      becho(ch->name + "'s and " + projectile->ch->name + "'s energy projectiles cancel each other out!\n\r", ch, projectile->ch);
      ch->printf("Your attacks cancelled each other out!\n\r");
      projectile->ch->printf("Your attacks cancelled each other out!\n\r");
      zap(projectile_this);
      zap(projectile);
    }
  }

  return;
}

void PROJECTILE::warn(CH *target) {
  if(target->status(status_unconscious))
    ctr = 1;
  else {
    ctr = 6;
    target->printf("{!WARNING{0: {#You have 3 seconds to defend against incoming energy attack!{0\n\r");
  }

  return;
}

void PROJECTILE::hunt(CH *target) {
  PROJECTILE *projectile_this = this;

  if(!target || !room) {
    zap(projectile_this);
    return;
  }

  if(target->room == room) {
    if(target->status(status_mirror)) {
      becho(ch->name + "'s projectile is reflected back at him!\n\r", ch, target);
      target->printf(ch->name + "'s projectile is reflected back at him!\n\r");
      ch->printf("Your projectile is reflected back at you!\n\r");
      this->target = ch;
      this->ch = target;
    }
    else
      warn(target);

    return;
  }

  for(short y = 0; y < spd; ++y) {
    move(dir(target));

    if(!this || !this->target || !room) {
      zap(projectile_this);
      return;
    }

    if(room == target->room) {
      if(target->status(status_mirror)) {
	string str = ch->name + "'s projectile is reflected back at him!\n\r";
        becho(str, ch, target);
        target->printf(str);
        ch->printf("Your projectile is reflected back at you!\n\r");
        this->target = ch;
        ch = target;
      }
      else
        warn(target);

      return;
    }
  }

  return;
}

short PROJECTILE::dir(const CH *ch) {
  bool north = false, south = false, east = false, west = false;
 
  if(ch->room->x > this->room->x)
    east = true;
  else if(ch->room->x < this->room->x)
    west = true;
 
  if(ch->room->y > this->room->y)
    south = true;
  else if(ch->room->y < this->room->y)
    north = true;
 
  return (north && east ? DIR_NE : north && west ? DIR_NW : south && east ? DIR_SE : south && west ? DIR_SW : north ? DIR_N : south ? DIR_S : east ? DIR_E : DIR_W);
}

void PROJECTILE::move(const short dir) {
  ROOM *to_room = NULL, *room = this->room;
  long id = 0, size = this->room->area->width * this->room->area->height;
 
  if(dir == DIR_N)        id = room->id - area->width;
  else if(dir == DIR_S)   id = room->id + area->width;
  else if(dir == DIR_E)   id = room->id + 1;
  else if(dir == DIR_W)   id = room->id - 1;
  else if(dir == DIR_NW)  id = room->id - area->width - 1;
  else if(dir == DIR_NE)  id = room->id - area->width + 1;
  else if(dir == DIR_SW)  id = room->id + area->width - 1;
  else if(dir == DIR_SE)  id = room->id + area->width + 1;
 
  if(id > size)
    id -= size;
  else if(id < 1)
    id += size;
 
  to_room = area->room[id];
 
  if(!area->wrap && to_room && ((abs)(to_room->x - room->x) > 22 || (abs)(to_room->y - room->y) > 22))
    to_room = NULL;
 
  if(!to_room)
    return;
 
  leaveTrail();
  room->echo("An energy wave flies " + (string)dir_name[dir] + ".\n\r");
  to(to_room);
  return;
}

void PROJECTILE::leaveTrail() {
  if(!trail)
    return;

  OBJ *obj = create_obj("energy trail");

  if(!obj)
    return;

  obj->invis = true;
  obj->to(this->room);
  obj->timer = 20;
  obj->color = this->color;

  if(ch->npc)
    obj->owner = ch;

  return;
}

void PROJECTILE::calculateDamage(const CH *target) {
  dam += dam * (ch->stats[STAT_STR] / 100);

  if(crit)
    dam += dam * (ch->stats[STAT_CRITDAM] / 10);

  dam -= dam * (target->stats[STAT_DUR] / 100);

  if(!dam)
    dam = 1;

  if(target->power_to)
    dam = 0;

  return;
}

void PROJECTILE::hit(CH *target) {
  calculateDamage(target);
  ostrstream ost;
  ost << " {3({1" << str_comma(dam) << "{3){0\n\r" << ends;
  room->necho(ost.str(), ch, target);
  ost.freeze(false);
  ost.seekp(0, ios::beg);
  ost << " {#({!" << str_comma(dam) << "{#){0\n\r" << ends;
  ch->printf(ost.str());
  target->printf(ost.str());
  ch->set_fighting(target);
  target->ftimer = ch->ftimer = 2;
  target->cancel(status_sleeping);
  target->cancel(status_resting);
  target->cancel(status_flying);

  if(!target->fighting)
    target->set_fighting(ch);
 
  if(target->powered_up)
    target->powered_up = MIN(target->powered_up - dam, 0);
 
  if((target->pl[0] -= dam) < 1) {
    if(target->npc && npc_table[target->npcID].onKO && !npc_table[target->npcID].onKO(ch, target))
      return;
    else
      target->die(ch);
  }

  if(ch && ch->attack.action == act_teleport)
    ch->clear_attack();

  return;
}

void CH::clear_incoming_projectiles() {
  for(PROJECTILE *b = projectile_list, *b_next; b; b = b_next) {
    b_next = b->next;
  
    if(b->target == this && !b->busy)
      zap(b);
  }
    
  return;
}

PROJECTILE *CH::create_projectile(ON_HIT *doHit, const short ctr, CH *target, const llong dam, const short color, const bool trail, const short spd = 1) {
  PROJECTILE *projectile = new PROJECTILE;
  projectile->next = projectile_list;
  projectile_list = projectile;
  projectile->ch = this;
  projectile->onHit = doHit;
  projectile->timer = ctr;
  projectile->target = target;
  projectile->dam = dam;   
  projectile->spd = spd;
  projectile->color = color;
  projectile->trail = trail;
  projectile->to(room);
  return projectile;
} 

PROJECTILE *CH::incoming_blast() {
  for(PROJECTILE *projectile = projectile_list; projectile; projectile = projectile->next)
    if(projectile->target == this && projectile->ctr)
      return projectile;
  return NULL;
}

bool CH::projectile() {
  for(PROJECTILE *projectile = projectile_list; projectile; projectile = projectile->next)
    if(projectile->ch && projectile->ch == this)
      return true;
  return false;
}

bool CH::can_see(const PROJECTILE *projectile) {
  if(admin || !room->area->solo || (room->area->solo && projectile->ch && (projectile->ch == this || projectile->target == this)))
    return true;
    
  return false;
}

PROJECTILE *ROOM::get_moving_projectile(const PROJECTILE *p = NULL) {
  for(PROJECTILE *projectile = this->projectile; projectile; projectile = projectile->next_in_room)
    if(!projectile->ctr && ((p && projectile != p) || !p) && ((area->solo && p && projectile->target && p->ch == projectile->target) || !area->solo))
      return projectile;
 
  return NULL;
}