/**************************************************************
* 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;
}