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