/* Copyright (C) 1995-2002 FSGames. Ported by Sean Ford and Yan Shosh * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //walker.cpp /* ChangeLog buffers: 7/31/02: *deleted some redundant headers */ #include "graph.h" #include "smooth.h" // ************************************************************ // WALKER -- graphics routines // // WALKER is a PIXIEN with automatic frame changing when // the direction it moves is changed. This allows for // the concept of "facings", though currently no query-able // variable allows for external functions to learn the facing. // ************************************************************ // From picker.cpp extern long calculate_level(unsigned long temp_exp); extern long difficulty_level[DIFFICULTY_SETTINGS]; extern long current_difficulty; walker::walker(unsigned char *data, screen *myscreen): pixieN(data, myscreen) { // Set our stats .. stats = new statistics(this); curdir = 0; // We are facing UP enddir = 0; // We are trying to face UP lastx = 0; lasty = 0; act_type = ACT_RANDOM; collide_ob = NULL; cycle = 0; ani = NULL; team_num = 0; real_team_num = 255; // to show nothing's changed ani_type = 0; busy = 0; foe = NULL; leader = NULL; owner = NULL; myguy = NULL; myself = this; ani = NULL; dead = 0; // we're alive death_called = 0; bonus_rounds = 0; shifter_down = 0; // the player's shifter/alternate is NOT pressed view_all = 0; // by default can't see treasures, etc. on radar keys = 0; // no keys action = 0; // no special action mode ignore = 0; // don't ignore us! Collide with us... default_weapon = current_weapon = FAMILY_KNIFE; // just in case .. user = -1; // default user status = no user // Set our stats .. set_frame(0); yo_delay = 0; flight_left = 0; invulnerable_left = 0; invisibility_left = 0; speed_bonus = 0; speed_bonus_left = 0; charm_left = 0; outline = 0; drawcycle = 0; skip_exit = 0; xpos = ypos = -1; //this to correct a problem with these not being alloced? weapons_left = 1; // default, used for fighters cachenext = NULL; } short walker::reset(void) { // double comments we're needed to make it work, maybe they are // not needed?? // curdir = 0; // We are facing UP // enddir = 0; // We are trying to face UP // lastx = 0; // lasty = 0; // act_type = ACT_RANDOM; // collide_ob = NULL; // // cycle = 0; // ani = NULL; // // team_num = 0; // // ani_type = 0; // // busy = 0; // foe = NULL; // leader = NULL; // owner = NULL; // myguy = NULL; // myself = this; // ani = NULL; dead = 0; // we're alive death_called = 0; // bonus_rounds = 0; // shifter_down = 0; // the player's shifter/alternate is NOT pressed // view_all = 0; // by default can't see treasures, etc. on radar // keys = 0; // no keys // action = 0; // no special action mode ignore = 0; // don't ignore us! Collide with us... // default_weapon = current_weapon = FAMILY_KNIFE; // just in case .. // user = -1; // default user status = no user // Set our stats .. // set_frame(0); // yo_delay = 0; flight_left = 0; // invulnerable_left = 0; // invisibility_left = 0; // outline = 0; // drawcycle = 0; // skip_exit = 0; // xpos = ypos = -1; //this to correct a problem with these not being alloced? // weapons_left = 1; // default, used for fighters if (stats) stats->bit_flags = 0; cachenext = NULL; return 1; } walker::~walker() { // printf("(Death) Removed ORDER %d FAMILY %d, pos %dx%d\n", order, family, // xpos, ypos); //debugging memory foe = NULL; leader = NULL; owner = NULL; collide_ob = NULL; dead = 1; screenp->myobmap->remove (this); // remove ourselves from obmap lists //screenp->myobmap->remove_all(this); // remove ALL cases, anywhere delete stats; stats = NULL; bmp = NULL; if (myguy) delete myguy; myguy = NULL; myself = NULL; } short walker::move(short x, short y) { return setxy((short) (xpos+x), (short) (ypos+y)); } short walker::setxy(short x, short y) { if (!ignore) screenp->myobmap->move(this, x, y); else // just remove us, in case :) screenp->myobmap->remove (this); return pixie::setxy(x, y); } // WALK -- This function allows us to change facing when we walk. // This includes an automatic frame change. It also redraws the background // at the coords it used to occupy. // It calls the lower level function MOVE. short walker::walk() { return walker::walk(lastx, lasty); } short walker::facing(short x, short y) { long bigy = y*1000; long slope; if (!x) if (y>0) return FACE_DOWN; else return FACE_UP; slope = bigy / x; if (x>0) { if (slope > 2414) return FACE_DOWN; if (slope > 414) return FACE_DOWN_RIGHT; if (slope > -414) return FACE_RIGHT; if (slope > -2414) return FACE_UP_RIGHT; return FACE_UP; } else { if (slope > 2414) return FACE_UP; if (slope > 414) return FACE_UP_LEFT; if (slope > -414) return FACE_LEFT; if (slope > -2414) return FACE_DOWN_LEFT; return FACE_DOWN; } } short walker::shove(walker *target, short x, short y) { // this code has been moved to living, we should only shove livings if (x || y || target) printf("Shoving a non-living. ORDER: %d FAMILY: %d\n",order,family); return -1; } short walker::walkstep(short x, short y) { short returnvalue; short ret1, ret2; short oldcurdir = curdir; short step = (short) stepsize; short halfstep; long i; long gotup = 0, gotover = 0; //walker *control1 = screenp->viewob[0]->control; //walker *control2; short mycycle; // Repeat last walk. lastx = (short) (x*stepsize); lasty = (short) (y*stepsize); if (order==ORDER_LIVING && family==FAMILY_TOWER1) { curdir = facing(x, y); enddir = curdir; lastx = x; lasty = y; return 1; } returnvalue = walk((short) (x*stepsize), (short) (y*stepsize)); halfstep = 1; if (!returnvalue) // couldn't walk this direction .. { returnvalue = walk((short) (x*halfstep), (short) (y*halfstep)); // Now try a baby step if (!returnvalue) // if we still fail { if (user == -1) // means we are an npc { switch (facing(x, y)) { case FACE_UP: // For cardinal directions, fail if curdir = FACE_LEFT; ret1 = walk(-step, 0); break; case FACE_RIGHT: // we can't walk this direction curdir = FACE_UP; ret1 = walk(0, (short) (-step)); break; case FACE_DOWN: curdir = FACE_RIGHT; ret1 = walk(step, 0); break; case FACE_LEFT: curdir = FACE_DOWN; ret1 = walk(0, step); break; //return returnvalue; case FACE_UP_RIGHT: curdir = FACE_UP; ret1 = walk(0, (short) (y*step)); curdir = FACE_RIGHT; ret2 = walk((short) (x*step), 0); break; case FACE_DOWN_RIGHT: curdir = FACE_DOWN; ret1 = walk(0, (short) (y*step)); curdir = FACE_RIGHT; ret2 = walk((short) (x*step), 0); break; case FACE_DOWN_LEFT: curdir = FACE_DOWN; ret1 = walk(0, (short) (y*step)); curdir = FACE_LEFT; ret2 = walk((short) (x*step), 0); break; case FACE_UP_LEFT: curdir = FACE_UP; ret1 = walk(0, (short) (y*step)); curdir = FACE_LEFT; ret2 = walk((short) (x*step), 0); break; default: ret1 = 0; ret2 = 0; break; } } // end of npc switch else // we're a user { // Store our cycle mycycle = cycle; switch (facing(x, y)) { case FACE_UP: // For cardinal directions, fail if case FACE_RIGHT: // we can't walk this direction case FACE_DOWN: case FACE_LEFT: //return returnvalue; break; case FACE_UP_RIGHT: for (i=0; i < step; i++) { if (screenp->query_passable(xpos, ypos-1, this)) { move(0, -1); // walk without turning .. gotup = 1; } if (screenp->query_passable(xpos+1, ypos, this)) { move(1, 0); gotover = 1; } if (!gotup && gotover) // moved right curdir = FACE_RIGHT; else if (gotup && !gotover) // moved up curdir = FACE_UP; if (gotup || gotover) // we moved somewhere? { cycle = mycycle; cycle++; if (ani[curdir][cycle] == -1) cycle = 0; set_frame(ani[curdir][cycle]); } // end of cycled us a frame } //curdir = FACE_UP; ret1 = walk(0, (short) (y*step)); //curdir = FACE_RIGHT; ret2 = walk((short) (x*step), 0); break; case FACE_DOWN_RIGHT: for (i=0; i < step; i++) { if (screenp->query_passable(xpos, ypos+1, this)) { move(0, 1); // walk without turning .. gotup = 1; } if (screenp->query_passable(xpos+1, ypos, this)) { move(1, 0); gotover = 1; } if (!gotup && gotover) // moved right curdir = FACE_RIGHT; else if (gotup && !gotover) // moved up curdir = FACE_DOWN; if (gotup || gotover) // we moved somewhere? { cycle = mycycle; cycle++; if (ani[curdir][cycle] == -1) cycle = 0; set_frame(ani[curdir][cycle]); } // end of cycled us a frame } //curdir = FACE_DOWN; ret1 = walk(0, (short) (y*step)); //curdir = FACE_RIGHT; ret2 = walk((short) (x*step), 0); break; case FACE_DOWN_LEFT: for (i=0; i < step; i++) { if (screenp->query_passable(xpos, ypos+1, this)) { move(0, 1); // walk without turning .. gotup = 1; } if (screenp->query_passable(xpos-1, ypos, this)) { move(-1, 0); gotover = 1; } if (!gotup && gotover) // moved right curdir = FACE_LEFT; else if (gotup && !gotover) // moved up curdir = FACE_DOWN; if (gotup || gotover) // we moved somewhere? { cycle = mycycle; cycle++; if (ani[curdir][cycle] == -1) cycle = 0; set_frame(ani[curdir][cycle]); } // end of cycled us a frame } //curdir = FACE_DOWN; ret1 = walk(0, (short) (y*step)); //curdir = FACE_LEFT; ret2 = walk((short) (x*step), 0); break; case FACE_UP_LEFT: for (i=0; i < step; i++) { if (screenp->query_passable(xpos, ypos-1, this)) { move(0, -1); // walk without turning .. gotup = 1; } if (screenp->query_passable(xpos-1, ypos, this)) { move(-1, 0); gotover = 1; } if (!gotup && gotover) // moved left curdir = FACE_LEFT; else if (gotup && !gotover) // moved up curdir = FACE_UP; if (gotup || gotover) // we moved somewhere? { cycle = mycycle; cycle++; if (ani[curdir][cycle] == -1) cycle = 0; set_frame(ani[curdir][cycle]); } // end of cycled us a frame } //curdir = FACE_UP; ret1 = walk(0, (short) (y*step)); //curdir = FACE_LEFT; ret2 = walk((short) (x*step), 0); break; default: ret1 = 0; ret2 = 0; break; } } curdir = (char) oldcurdir; return ( ret1 || ret2 ); } } return returnvalue; } short walker::walk(short x, short y) { short dir; dir = facing(x, y); if (order==ORDER_LIVING && family==FAMILY_TOWER1) { curdir = dir; return 1; } if ( !x && !y) { //printf("walker %d:%d walking 0,0\n",order, family); //this happens sometimes, and shouldn't, but it is non-fatal return 1; } if (curdir == dir) // if continue direction { // check if off map if (x+xpos < 0 || x+xpos >= screenp->maxx*GRID_SIZE || y+ypos < 0 || y+ypos >= screenp->maxy*GRID_SIZE) { return 0; } // Here we check if the move is valid if (screenp->query_passable((short) (xpos+x),(short) (ypos+y),this)) { // Control object does complete redraw anyway move(x,y); cycle++; //if (!ani || (curdir*cycle > sizeof(ani)) ) // printf("WALKER::WALK: Bad ani!\n"); if (ani[curdir][cycle] == -1) cycle = 0; set_frame(ani[curdir][cycle]); return 1; } else //Invalid move? { //we're not alive if (stats->query_bit_flags(BIT_ANIMATE) ) // animate regardless.. { cycle++; if (ani[curdir][cycle] == -1) cycle = 0; set_frame(ani[curdir][cycle]); } return 0; } } else // changed direction { curdir = (char) dir; cycle = 0; set_frame(ani[curdir][cycle]); move(0,0); } return 1; } short walker::turn(short targetdir) { short distance; // We use a clock-ordered // of directions to numbers) to a clock-ordered // mapping of directions so we can calculate what // our next facing should be based on our current one. // Find how we have to turn. distance = (short) (curdir - targetdir); // Figure out if we should turn clockwise or counterclockwise if ( ( (distance >= -4) && (distance < 0) ) || (distance >= 4) ) curdir = (char) ((curdir+1) %8); else curdir = (char) ((curdir+7) %8); // Now set our lastx and lasty (facing) variables correctly if ( (order!=ORDER_LIVING) || (family!=FAMILY_TOWER1) ) { switch (curdir) { case FACE_UP: lastx = 0; lasty = -stepsize; break; case FACE_UP_RIGHT: lastx = stepsize; lasty = -stepsize; break; case FACE_RIGHT: lastx = stepsize; lasty = 0; break; case FACE_DOWN_RIGHT: lastx = stepsize; lasty = stepsize; break; case FACE_DOWN: lastx = 0; lasty = stepsize; break; case FACE_DOWN_LEFT: lastx = -stepsize; lasty = stepsize; break; case FACE_LEFT: lastx = -stepsize; lasty = 0; break; case FACE_UP_LEFT: lastx = -stepsize; lasty = -stepsize; break; default : lastx = 0; lasty = -stepsize; } } cycle = 0; set_frame(ani[curdir][cycle]); move(0,0); return 1; } // This is the function you actually call when you want something // to fire. It initializes the animation if animation is valid // and checks to see if the object is too busy. short walker::init_fire() { return init_fire(lastx, lasty); } short walker::init_fire(short xdir, short ydir) { // Turn if we want to fire another direction // If a non-player fires in a set direction, turn! if (facing(xdir, ydir) != curdir) { enddir = (char) facing(xdir, ydir); } if (curdir != enddir && query_order() == ORDER_LIVING) { //if (family==FAMILY_TOWER1) // enddir = curdir; if (query_act_type() == ACT_CONTROL) return 0; else return turn(enddir); } if (busy > 0) return 0; // Too busy if ( (order==ORDER_LIVING) && (family==FAMILY_SOLDIER) ) if (weapons_left < 1) return 0; busy += fire_frequency; // This pauses a few rounds // if (ani_type == ANI_WALK && query_order() == ORDER_LIVING) if (ani_type == ANI_WALK) // This should allow generators to animate { ani_type = ANI_ATTACK; cycle = 0; animate(); return 1; } else { if (fire()) return 1; else return 0; } } walker * walker::fire() { walker *weapon = NULL; signed char waver; short xp, yp; long extra; // Do we have enough spellpoints for our weapon if (stats->magicpoints < stats->weapon_cost) return NULL; if ( (order==ORDER_LIVING) && (family==FAMILY_SOLDIER) && (weapons_left < 1) ) return NULL; weapon = create_weapon(); if (!weapon) return NULL; stats->magicpoints -= stats->weapon_cost; // Determine how much the thrown weapon can 'waver' waver = (signed char) ((weapon->stepsize)/2); // Absolute amount .. waver = (signed char) (random(waver+1) - waver/2); switch(facing(lastx, lasty)) { case FACE_RIGHT: weapon->setxy(xpos+sizex+1,ypos+(sizey - weapon->sizey)/2); weapon->lastx = weapon->stepsize; weapon->lasty = waver; break; case FACE_LEFT: weapon->setxy(xpos - weapon->sizex-1, ypos+(sizey-weapon->sizey)/2); weapon->lastx = -weapon->stepsize; weapon->lasty = waver; break; case FACE_DOWN: weapon->setxy(xpos+(sizex-weapon->sizex)/2, ypos+sizey+1); weapon->lasty = weapon->stepsize; weapon->lastx = waver; break; case FACE_UP: weapon->setxy(xpos+(sizex-weapon->sizex)/2, ypos - weapon->sizey-1); weapon->lasty = - weapon->stepsize; weapon->lastx = waver; break; case FACE_UP_RIGHT: weapon->setxy(xpos+sizex+1, ypos-weapon->sizey-1); weapon->lastx = weapon->stepsize + waver; weapon->lasty = -weapon->stepsize + waver; break; case FACE_UP_LEFT: weapon->setxy(xpos - weapon->sizex-1, ypos-weapon->sizey-1); weapon->lastx = -weapon->stepsize - waver; weapon->lasty = -weapon->stepsize + waver; break; case FACE_DOWN_RIGHT: weapon->setxy(xpos+sizex+1, ypos + sizey+1); weapon->lasty = weapon->stepsize + waver; weapon->lastx = weapon->stepsize - waver; break; case FACE_DOWN_LEFT: weapon->setxy(xpos - weapon->sizex-1, ypos+sizey+1); weapon->lasty = weapon->stepsize + waver; weapon->lastx = -weapon->stepsize + waver; break; } weapon->set_frame(frame); // Make sure our current direction is wrong so first walk // will just be draw (grumble curse) weapon->curdir = (char) ((frame+1)%2); xp = weapon->xpos; yp = weapon->ypos; // Actual combat if (!screenp->query_passable(weapon->xpos, weapon->ypos, weapon)) { // *** Melee combat *** if (weapon->collide_ob && !weapon->collide_ob->dead) { if (attack(weapon->collide_ob) && on_screen() ) { screenp->soundp->play_sound(SOUND_CLANG); } if (myguy) myguy->total_shots +=1; // record that we fired/attacked } weapon->dead = 1; return NULL; } else if (stats->query_bit_flags(BIT_NO_RANGED)) { weapon->dead = 1; return NULL; } else { // Record our shot .. if (myguy) myguy->total_shots += 1; if (order==ORDER_LIVING && family==FAMILY_SOLDIER) weapons_left--; // *** Ranged combat *** if (on_screen()) { if (weapon->query_family() == FAMILY_FIREBALL) screenp->soundp->play_sound(SOUND_BLAST); else if (weapon->query_family() == FAMILY_METEOR) //play_sound(SOUND_FIREBALL); screenp->soundp->play_sound(SOUND_BLAST); else if (weapon->query_family() == FAMILY_SPRINKLE) //play_sound(SOUND_SPARKLE); screenp->soundp->play_sound(SOUND_SPARKLE); else if (weapon->query_family() == FAMILY_ARROW) //play_sound(SOUND_FIRE); screenp->soundp->play_sound(SOUND_BOW); else if (weapon->query_family() == FAMILY_FIRE_ARROW) //play_sound(SOUND_FIRE); screenp->soundp->play_sound(SOUND_BOW); else if (weapon->query_family() == FAMILY_LIGHTNING) screenp->soundp->play_sound(SOUND_BOLT); else //play_sound(SOUND_FWIP); screenp->soundp->play_sound(SOUND_FWIP); } if (order == ORDER_GENERATOR) { switch (family) { case FAMILY_TOWER: // mages, no lifetime weapon->ani_type = ANI_TELE_IN; // mages teleport case FAMILY_TREEHOUSE: // elves also no lifetime weapon->stats->level = random(stats->level)+1; weapon->set_difficulty( (unsigned long) weapon->stats->level ); weapon->owner = NULL; break; default: // tents, bones, etc weapon->lifetime = 800 + stats->level*11; weapon->stats->level = random(stats->level)+1; weapon->set_difficulty((unsigned long) weapon->stats->level); break; } } else if (order == ORDER_LIVING) { switch (family) { case FAMILY_ARCHMAGE: // ArchMage gets 1/20th of 'extra' // magic for more damage ... extra = stats->magicpoints / 20; stats->magicpoints -= extra; weapon->damage += extra; // get this in damage break; default: // do nothing break; } // end of switch for living family } // end of switch for living return weapon; } } void walker::set_weapon_heading(walker *weapon) { signed char waver; // Determine how much the thrown weapon can 'waver' waver = (signed char) ((weapon->stepsize)/2); // Absolute amount .. waver = (signed char) (random(waver+1) - waver/2); switch(facing(lastx, lasty)) // these are from the 'owner' { case FACE_RIGHT: weapon->setxy(xpos+sizex+1,ypos+(sizey - weapon->sizey)/2); weapon->lastx = weapon->stepsize; weapon->lasty = waver; break; case FACE_LEFT: weapon->setxy(xpos - weapon->sizex-1, ypos+(sizey-weapon->sizey)/2); weapon->lastx = -weapon->stepsize; weapon->lasty = waver; break; case FACE_DOWN: weapon->setxy(xpos+(sizex-weapon->sizex)/2, ypos+sizey+1); weapon->lasty = weapon->stepsize; weapon->lastx = waver; break; case FACE_UP: weapon->setxy(xpos+(sizex-weapon->sizex)/2, ypos - weapon->sizey-1); weapon->lasty = - weapon->stepsize; weapon->lastx = waver; break; case FACE_UP_RIGHT: weapon->setxy(xpos+sizex+1, ypos-weapon->sizey-1); weapon->lastx = weapon->stepsize + waver; weapon->lasty = -weapon->stepsize + waver; break; case FACE_UP_LEFT: weapon->setxy(xpos - weapon->sizex-1, ypos-weapon->sizey-1); weapon->lastx = -weapon->stepsize - waver; weapon->lasty = -weapon->stepsize + waver; break; case FACE_DOWN_RIGHT: weapon->setxy(xpos+sizex+1, ypos + sizey+1); weapon->lasty = weapon->stepsize + waver; weapon->lastx = weapon->stepsize - waver; break; case FACE_DOWN_LEFT: weapon->setxy(xpos - weapon->sizex-1, ypos+sizey+1); weapon->lasty = weapon->stepsize + waver; weapon->lastx = -weapon->stepsize + waver; break; } } short walker::draw(viewscreen *view_buf) { long xscreen, yscreen; //no need for on screen check, it will be checked at the draw level //and the draw level code is cleaner anyway //if (!this) return 0; if (dead) { printf("drawing a dead guy!\n"); return 0; } //if (!bmp) {printf("No bitmap!\n"); return 0;} drawcycle++; xscreen = (long) (xpos - view_buf->topx + view_buf->xloc); yscreen = (long) (ypos - view_buf->topy + view_buf->yloc); if (stats->query_bit_flags( BIT_NAMED ) || invisibility_left || flight_left || invulnerable_left) { if (outline == OUTLINE_INVULNERABLE) { if (flight_left) outline = OUTLINE_FLYING; else if (view_buf->control) if (stats->query_bit_flags (BIT_NAMED) && (team_num!=view_buf->control->team_num)) outline = OUTLINE_NAMED; if (outline != OUTLINE_NAMED) if (invisibility_left) outline = OUTLINE_INVISIBLE; } else if (outline == OUTLINE_FLYING) { //if (stats->query_bit_flags (BIT_NAMED) && (team_num!=view_buf->control->team_num)) outline = OUTLINE_NAMED; //else if (invisibility_left) outline = OUTLINE_INVISIBLE; //else if (invulnerable_left) outline = OUTLINE_INVULNERABLE; if (view_buf->control) if (stats->query_bit_flags (BIT_NAMED) && (team_num!=view_buf->control->team_num)) outline = OUTLINE_NAMED; if (outline != OUTLINE_NAMED) { if (invisibility_left) outline = OUTLINE_INVISIBLE; else if (invulnerable_left) outline = OUTLINE_INVULNERABLE; } } else if (outline == OUTLINE_NAMED) { if (invisibility_left) outline = OUTLINE_INVISIBLE; else if (invulnerable_left) outline = OUTLINE_INVULNERABLE; else if (flight_left) outline = OUTLINE_FLYING; } else if (outline == OUTLINE_INVISIBLE) { if (invulnerable_left) outline = OUTLINE_INVULNERABLE; else if (flight_left) outline = OUTLINE_FLYING; else if (view_buf->control) if (stats->query_bit_flags (BIT_NAMED) && (team_num!=view_buf->control->team_num)) outline = OUTLINE_NAMED; } else { if (invisibility_left) outline = OUTLINE_INVISIBLE; else if (flight_left) outline = OUTLINE_FLYING; else if (invulnerable_left) outline = OUTLINE_INVULNERABLE; else if (view_buf->control) if (stats->query_bit_flags (BIT_NAMED) && (team_num!=view_buf->control->team_num)) outline = OUTLINE_NAMED; } } else outline = 0; if (stats->query_bit_flags(BIT_PHANTOM)) //WE ARE A PHANTOM screenp->walkputbuffer( xscreen, yscreen, sizex, sizey, view_buf->xloc, view_buf->yloc, view_buf->endx, view_buf->endy, bmp, query_team_color(), PHANTOM_MODE, //mode 0, //invisibility 0, //outline SHIFT_RANDOM); //type of phantom else if (invisibility_left) //WE ARE INVISIBLE { if (this->team_num == view_buf->control->team_num) screenp->walkputbuffer( xscreen, yscreen, sizex, sizey, view_buf->xloc, view_buf->yloc, view_buf->endx, view_buf->endy, bmp, query_team_color(), INVISIBLE_MODE, //mode ( invisibility_left + 10 ), //invisibility outline, //outline 0 ); //type of phantom } else if (stats->query_bit_flags(BIT_FORESTWALK) && mysmoother && mysmoother->query_genre_x_y(xpos/GRID_SIZE, ypos/GRID_SIZE) == TYPE_TREES && !stats->query_bit_flags(BIT_FLYING) && (flight_left < 1) ) screenp->walkputbuffer( xscreen, yscreen, sizex, sizey, view_buf->xloc, view_buf->yloc, view_buf->endx, view_buf->endy, bmp, query_team_color(), INVISIBLE_MODE, //mode 1000, //invisibility 1, //outline 0 ); //type of phantom else if (outline) // WE HAVE SOME OUTLINE screenp->walkputbuffer( xscreen, yscreen, sizex, sizey, view_buf->xloc, view_buf->yloc, view_buf->endx, view_buf->endy, bmp, query_team_color(), OUTLINE_MODE, //mode 0, //invisibility outline, //outline 0 ); //type of phantom else screenp->walkputbuffer(xscreen, yscreen, sizex, sizey, view_buf->xloc, view_buf->yloc, view_buf->endx, view_buf->endy, bmp, query_team_color()); return 1; } short walker::act() { short temp; // Make sure everyone we're pointing to is valid if (foe && foe->dead) foe = NULL; if (leader && leader->dead) leader = NULL; if (owner && owner->dead) owner = NULL; collide_ob = NULL; // always start with no collison.. // Complete previous animations (like firing) if (ani_type != ANI_WALK) return animate(); // Are we frozen? if (stats->frozen_delay) { stats->frozen_delay--; return 1; } if (busy > 0) busy--; // This allows busy to be our FIRING delay. // Find new action // Turn if you want to // if (curdir != enddir && query_order() == ORDER_LIVING) // return turn(enddir); // No actions for us if we are ACT_CONTROL! // if (act_type == ACT_CONTROL) // stats->clear_command(); // Are we performing some action? if (stats->commandlist) { temp = stats->do_command(); if (temp) return 1; } switch (act_type) { // We are the control character case ACT_CONTROL: { return 1; //break; } // We are a generator case ACT_GENERATE: { act_generate(); break; } // We are a weapon case ACT_FIRE: { act_fire(); return 1; //break; } case ACT_GUARD: { act_guard(); break; } case ACT_DIE: { this->dead = 1; return 1; } // We are randomly walking toward enemy case ACT_RANDOM: { if (!random(4) ) { if (!random(20)) // a 1 in 4 then 1 in 20 chance of rand walk { if (!special()) stats->try_command(COMMAND_WALK, random(30), random(3)-1, random(3)-1); return 1; } act_random(); //1 in 4 followed by 19 in 20 of doing this } else //3 of 4 times { if (!foe) { foe = screenp->find_far_foe(this); } if (foe) //stats->try_command(COMMAND_SEARCH, 60, 0, 0); stats->try_command(COMMAND_SEARCH, 500, 0, 0); return 1; } } // END RANDOM break; default: { printf("No act type set.\n"); return 0; } } // END SWITCH return 0; } short walker::set_act_type(short num) { old_act_type = act_type; act_type = (char) num; return num; } short walker::restore_act_type() { act_type = old_act_type; return old_act_type; } short walker::query_act_type() { return act_type; } short walker::set_old_act_type(short num) { old_act_type = (char) num; return num; } short walker::query_old_act_type() { return old_act_type; } short walker::collide(walker *ob) { collide_ob = ob; return 1; } short walker::attack(walker *target) { walker *blood; // temporary stain walker *headguy; // guy at top of chain.. short playerteam = -1; char message[80]; long tempdamage = damage; short newexp; short getscore=0; char targetorder = target->query_order(); char targetfamily= target->query_family(); walker *attacker; // us or our owner .. static short tom = 0; if (myguy != NULL || team_num == 0) getscore = 1; if (target && target->dead) return 0; //if ( (targetorder == ORDER_LIVING && is_friendly(target) ) || if ( is_friendly(target) || (targetorder == ORDER_TREASURE) ) return 0; if (target->stats->query_bit_flags(BIT_INVINCIBLE) || target->invulnerable_left != 0 ) return 0; if (order != ORDER_LIVING && owner) attacker = owner; else attacker = this; // who's the top on our chain (ie, weapon->summoned->mage) headguy = this; while (headguy->owner && (headguy->owner != headguy) ) headguy = headguy->owner; if (headguy->myguy && headguy->user == 0 && order == ORDER_WEAPON) tom++; // Modify attack value based on things like magical attacks, etc. switch (targetorder) // generally going to be livings.. { case ORDER_LIVING: // Hit a living target, so we get credit for a hit if (attacker->myguy) attacker->myguy->total_hits +=1; switch (targetfamily) { case FAMILY_SLIME: // Slimes are hurt MORE by case FAMILY_SMALL_SLIME: // magical or fire weapons case FAMILY_MEDIUM_SLIME: if (stats->query_bit_flags(BIT_MAGICAL)) tempdamage *= 2; // twice as susceptible to magic.. break; // end of slimes case FAMILY_BARBARIAN: // Barbarians get LESS damaged if (stats->query_bit_flags(BIT_MAGICAL)) // by magical attacks tempdamage /= 2; break; default: break; // do nothing in default living case } // end of living target case break; // end of living default: // We hit something, but it wasn't living, so don't count // as a shot, OR as a hit .. if (attacker->myguy) attacker->myguy->total_shots -= 1; // since we already counted it break; } // end of checking orders tempdamage -= random(target->stats->armor); if (tempdamage < 0) tempdamage = 0; // Record damage done for records .. if (attacker->myguy && targetorder==ORDER_LIVING) // hit a living attacker->myguy->total_damage += tempdamage; target->stats->hitpoints -= tempdamage; if (target->stats->hitpoints < 0) tempdamage += target->stats->hitpoints; newexp = tempdamage * (target->stats->level + 2)/2; if (order == ORDER_WEAPON && owner) newexp /= owner->stats->level; else newexp /= stats->level; if (newexp < 1) newexp = 1; // Assign bonus for non-player-controlled characters if (order == ORDER_WEAPON && owner) // we're a weapon { if (owner->query_act_type() != ACT_CONTROL) newexp *= 3; } else if (order == ORDER_LIVING) // we're a character if (act_type != ACT_CONTROL) newexp *= 3; // Set our target to fighting our owner //in the case of our weapon hit something if (order != ORDER_LIVING && owner) { owner->foe = target; target->stats->hit_response(owner); if (headguy->myguy) if (targetorder != ORDER_LIVING) headguy->myguy->exp += newexp/3; else headguy->myguy->exp += newexp; } else //melee combat, set target to hit_response to us { target->stats->hit_response(this); if (myguy) if (targetorder != ORDER_LIVING) myguy->exp += newexp/3; else { myguy->exp += newexp; if (getscore) { screenp->m_score[team_num] += tempdamage + target->stats->level; } } } if (order == ORDER_WEAPON) { stats->hitpoints -= tempdamage; damage--; if (stats->hitpoints <= 0) { if (!stats->query_bit_flags(BIT_IMMORTAL)) dead = 1; death(); } //special effects switch (query_family()) { case FAMILY_SPRINKLE: // Faerie's fire freezes foes :) if (targetorder == ORDER_LIVING) { if (target->myguy) target->stats->frozen_delay = random(FAERIE_FREEZE_TIME + (owner->stats->level*2) - (target->myguy->constitution/21) ); else target->stats->frozen_delay = random (FAERIE_FREEZE_TIME + (owner->stats->level*2) ); if (target->stats->frozen_delay < 0) target->stats->frozen_delay = 0; } break; default : break; } } playerteam = 0; // Positive score for hurting enemies, negative for us if (owner && (targetorder != ORDER_WEAPON) ) // are we still alive? { if (playerteam != target->team_num) { if (getscore) { screenp->m_score[team_num] += tempdamage + target->stats->level; // / 2; } if (headguy->myguy) headguy->myguy->exp += newexp; } } if (target->stats->hitpoints <= 0) { if (targetorder == ORDER_LIVING) { if (playerteam > -1) { if (playerteam != target->team_num) { if (headguy->myguy) // headguy can == this { headguy->myguy->exp += newexp + (8 * target->stats->level); headguy->myguy->kills++; headguy->myguy->level_kills += target->stats->level; } //else if (myguy) //{ // myguy->exp += newexp + (8 * target->stats->level); // myguy->kills++; // myguy->level_kills += target->stats->level; //} if (getscore) { screenp->m_score[team_num] += tempdamage + (10 * target->stats->level); } // If named, alert us of the enemy's death if (strlen(target->stats->name) && !(target->lifetime) && (!target->owner) ) // do we have an NPC name? { sprintf(message, "ENEMY DEATH: %s DIED!", target->stats->name); screenp->viewob[0]->set_display_text(message, STANDARD_TEXT_TIME); } } else { // Alert us of the death if ( (target->owner || target->lifetime) // summoned? && (strlen(target->stats->name) ) ) // and have name sprintf(message, "%s Dispelled!", target->stats->name); else if (strlen(target->stats->name)) // do we have an NPC name? sprintf(message, "%s DIED!", target->stats->name); else if (target->myguy && strlen(target->myguy->name) ) sprintf(message, "%s Died!", target->myguy->name); else switch (target->query_family()) { case FAMILY_SOLDIER: strcpy(message, "SOLDIER SLAIN"); break; case FAMILY_ARCHER: strcpy(message, "ARCHER DIED"); break; case FAMILY_THIEF: strcpy(message, "THIEF KILLED"); break; case FAMILY_ELF: strcpy(message, "ELF KILLED"); break; case FAMILY_MAGE: strcpy(message, "MAGE DIED"); break; case FAMILY_SKELETON: strcpy(message, "SKELETON CRUMBLED"); break; case FAMILY_CLERIC: strcpy(message, "CLERIC DIED"); break; case FAMILY_FIREELEMENTAL: strcpy(message, "FIRE ELEMENTAL EXTINGUISHED"); break; case FAMILY_FAERIE: strcpy(message, "FAERIE POPPED"); break; case FAMILY_SMALL_SLIME: case FAMILY_MEDIUM_SLIME: case FAMILY_SLIME: strcpy(message, "SLIME DESTROYED"); break; case FAMILY_GHOST: strcpy(message, "GHOST VANISHED"); break; case FAMILY_DRUID: strcpy(message,"DRUID VANQUISHED"); break; case FAMILY_ORC: strcpy(message,"ORC DIED"); break; default : strcpy(message, "SOMEONE DIED"); break; } screenp->viewob[0]->set_display_text(message, STANDARD_TEXT_TIME); } } /* Blood splats at death */ // Make temporary stain: blood = screenp->add_ob(ORDER_WEAPON, FAMILY_BLOOD); blood->team_num = target->team_num; blood->ani_type = ANI_GROW; blood->ignore = 1; // so that we can be walked over .. ? blood->setxy(target->xpos,target->ypos); } if (on_screen() && targetorder == ORDER_LIVING) { if (random(2)) screenp->soundp->play_sound(SOUND_DIE1); else screenp->soundp->play_sound(SOUND_DIE2); } target->dead = 1; target->death(); // any special effect upon death .. } collide_ob = NULL; return 1; } short walker::animate() { walker * newob; set_frame(ani[curdir+ani_type*NUM_FACINGS][cycle]); cycle++; if (ani[curdir+ani_type*NUM_FACINGS][cycle] == -1) { // if (ani_type == ANI_ATTACK && // query_order() == ORDER_LIVING) if (ani_type == ANI_ATTACK) { fire(); ani_type = ANI_WALK; cycle = 0; return 1; } // finished teleport out sequence // if (ani_type == ANI_SKEL_GROW && family == FAMILY_SKELETON) if (ani_type == ANI_SKEL_GROW && query_type(ORDER_LIVING, FAMILY_SKELETON)) { ani_type = ANI_WALK; cycle = 0; return 1; } if (ani_type == ANI_TELE_OUT && order == ORDER_LIVING) { if (family == FAMILY_MAGE || family==FAMILY_ARCHMAGE) { ani_type = ANI_TELE_IN; cycle = 0; teleport(); return 1; } else if (family == FAMILY_SKELETON) { ani_type = ANI_TELE_IN; cycle = 0; teleport_ranged(stats->level*18); return 1; } else { ani_type = ANI_WALK; cycle = 0; return 0; } } // Were we a slime who just split? if (ani_type == ANI_SLIME_SPLIT && order == ORDER_LIVING) { ani_type = ANI_WALK; cycle = 0; // First, shrink (and move) normal guy .. transform_to(ORDER_LIVING, FAMILY_SMALL_SLIME); setxy(xpos-10, ypos+10); // diagonal 'down left' of normal // Create a new small slime .. newob = screenp->add_ob(ORDER_LIVING, FAMILY_SMALL_SLIME); newob->setxy(xpos+12, ypos-12); // Transfer stats/etc. across to new guy .. //stats->magicpoints -= stats->special_cost[0]; transfer_stats(newob); if (newob->myguy && newob->myguy->exp < (1000*stats->level) ) { delete newob->myguy; // can't be 'sustained' if too low newob->myguy = NULL; strcpy(newob->stats->name, "SLIME"); // generic name newob->stats->level = calculate_level(myguy->exp/2); } else if (newob->myguy) // split our experience { myguy->exp /= 2; newob->myguy->exp /= 2; newob->myguy->level = calculate_level(newob->myguy->exp); newob->stats->level = newob->myguy->level; } newob->team_num = team_num; newob->foe = foe; newob->leader = leader; return 1; } ani_type = ANI_WALK; cycle = 0; } return 1; } short walker::set_order_family(char neworder, char newfamily) { order = neworder; family = newfamily; return 1; } walker *walker::create_weapon() { walker *weapon; short weapon_type; // Special case for generators if (query_order() == ORDER_GENERATOR) { weapon = screenp->add_ob(ORDER_LIVING, (char) default_weapon); weapon->team_num = team_num; weapon->owner = this; weapon->set_difficulty(stats->level); return weapon; } // Normally, only livings fire weapon_type = current_weapon; weapon = screenp->add_ob(ORDER_WEAPON, (char) weapon_type); weapon->team_num = team_num; weapon->owner = this; weapon->set_difficulty(stats->level); weapon->damage = ( (weapon->damage * (stats->level+3))/4 ); if (myguy) { weapon->lineofsight += (myguy->strength / 23) + (myguy->dexterity / 31); weapon->damage += (myguy->strength / 7); } else { weapon->damage *= stats->level; } weapon->lineofsight += (stats->level / 3); switch ( facing(lastx, lasty) ) // make 'circular' ranges { case FACE_UP: case FACE_RIGHT: case FACE_DOWN: case FACE_LEFT: // this will multiply by 1.207 .. weapon->lineofsight *= 309; weapon->lineofsight /= 256; // = 1.207 for circular range // this will multiply by 1.414 weapon->stepsize *= 362; weapon->stepsize /= 256; break; default : break; } if (query_family() == FAMILY_CLERIC) { weapon->ani_type = ANI_GLOWGROW; weapon->lifetime += (stats->level * 110); } // if (query_family() == FAMILY_DRUID) // weapon->ani_type = ANI_GROW; //duhhhh he's not using this as his normal weapon return weapon; } short walker::query_next_to() { short newx, newy; newx = xpos; newy = ypos; if (lastx > 0) newx += sizex; else if (lastx < 0) newx += -sizex; if (lasty > 0) newy += sizey; else //if (lasty < 0) newy += -sizey; if (!screenp->query_object_passable(newx, newy, this)) { return 1; } else { return 0; } } short walker::special() { walker * newob; weap * fireob; walker * alive, *tempwalk; short tempx, tempy; short i, j; short targetx, targety; unsigned long distance; oblink *newlist, *here; oblink *list2; short howmany; short didheal; short generic, generic2; char message[80], tempstr[80]; short person; // Are we somehow dead already? if (dead) { printf("Dead guy doing special!\n"); return 0; } // Do we have a stats object? If not, freak out and exit :) if (!stats) { printf("Special with no stats\n"); return 0; } // Do we have enough for our special ability? if (stats->magicpoints < stats->special_cost[current_special]) return 0; if (query_order() != ORDER_LIVING) { return 0; } switch (query_family()) { case FAMILY_ARCHER: switch(current_special) { case 1: // fire arrows tempx = lastx; tempy = lasty; curdir = -1; lastx = 0; lasty = 0; stats->magicpoints += (8*stats->weapon_cost); stats->add_command(COMMAND_SET_WEAPON, 1, FAMILY_FIRE_ARROW, 0); stats->add_command(COMMAND_QUICK_FIRE, 1, 0, -1); stats->add_command(COMMAND_QUICK_FIRE, 1, 1, -1); stats->add_command(COMMAND_QUICK_FIRE, 1, 1, 0); stats->add_command(COMMAND_QUICK_FIRE, 1, 1, 1); stats->add_command(COMMAND_QUICK_FIRE, 1, 0, 1); stats->add_command(COMMAND_QUICK_FIRE, 1, -1, 1); stats->add_command(COMMAND_QUICK_FIRE, 1, -1, 0); stats->add_command(COMMAND_QUICK_FIRE, 1, -1, -1); // stats->add_command(COMMAND_WALK, 1, tempx/stepsize, tempy/stepsize); stats->add_command(COMMAND_RESET_WEAPON, 1, 0, 0); break; case 2: // flurry of arrows if (busy) return 0; stats->magicpoints += (3*stats->weapon_cost); fire(); fire(); fire(); busy += (fire_frequency * 2); break; case 3: // exploding arrows case 4: default: if (busy) return 0; generic = current_weapon; current_weapon = FAMILY_FIRE_ARROW; newob = fire(); current_weapon = generic; if (!newob) return 0; // failsafe newob->skip_exit = 5000; // used as a dummy variable to // signify exploding .. :( newob->stats->hitpoints = 500; // buffed arrows newob->damage *= 2; break; } break; // end of archer case FAMILY_SOLDIER: switch (current_special) { case 1: // charge enemy if (!stats->forward_blocked()) { stats->add_command(COMMAND_RUSH, 3, lastx/stepsize, lasty/stepsize); if (on_screen()) screenp->soundp->play_sound(SOUND_CHARGE); } else return 0; break; case 2: // boomerang newob = screenp->add_ob(ORDER_FX, FAMILY_BOOMERANG); newob->owner = this; newob->team_num = team_num; newob->ani_type = 1; // dummy, non-zero value newob->lifetime = 30 + (stats->level)*12; newob->stats->hitpoints += stats->level*12; newob->stats->max_hitpoints = newob->stats->hitpoints; newob->damage += stats->level*4; break; case 3: // whirlwind attack if (busy) return 0; // can't do while attacking, etc. busy += 8; tempx = lastx; tempy = lasty; curdir = -1; lastx = 0; lasty = 0; //stats->magicpoints += (8*stats->weapon_cost); stats->add_command(COMMAND_WALK, 1, 0, -1); stats->add_command(COMMAND_WALK, 1, 1, -1); stats->add_command(COMMAND_WALK, 1, 1, 0); stats->add_command(COMMAND_WALK, 1, 1, 1); stats->add_command(COMMAND_WALK, 1, 0, 1); stats->add_command(COMMAND_WALK, 1, -1, 1); stats->add_command(COMMAND_WALK, 1, -1, 0); stats->add_command(COMMAND_WALK, 1, -1, -1); newlist = screenp->find_foes_in_range(screenp->oblist, 32+stats->level*2, &howmany, this); here = newlist; while (here) { if (here->ob) { tempx = here->ob->xpos - xpos; if (tempx) tempx = tempx / (abs(tempx)); tempy = here->ob->ypos - ypos; if (tempy) tempy = tempy / (abs(tempy)); attack(here->ob); here->ob->stats->force_command(COMMAND_WALK, 8, tempx, tempy); } here = here->next; } delete newlist; newlist = NULL; break; // end of whirlwind attack case 4: // Disarm opponent if (busy) return 0; if (!stats->forward_blocked()) return 0; // can't do this if no frontal enemy newlist = screenp->find_foes_in_range(screenp->oblist, 28, &howmany, this); if (!newlist) return 0; generic = 0; here = newlist; while (here) { if (here->ob) { if (random(stats->level) >= random(here->ob->stats->level)) here->ob->busy += 6*(stats->level - here->ob->stats->level + 1); generic = 1; // disarmed at least one guy } here = here->next; } delete_list(newlist); if (generic) { if (on_screen()) screenp->soundp->play_sound(SOUND_CHARGE); if (team_num == 0 || myguy) // player's team screenp->do_notify("Fighter Disarmed Enemy!", this); busy += 5; } else return 0; break; default: break; } break; // end of fighter case FAMILY_CLERIC: switch (current_special) { case 1: // heal / mystic mace if (!shifter_down) // then do normal heal { newlist = screenp->find_friends_in_range(screenp->oblist, 60, &howmany, this); didheal = 0; if (howmany > 1) // some friends here .. { here = newlist; while (here) { newob = here->ob; if (newob->stats->hitpoints < newob->stats->max_hitpoints && newob != this ) { generic = random(stats->level*5); newob->stats->hitpoints += generic; if (myguy) myguy->exp += (random(20*generic)/stats->level); didheal++; } here = here->next; } if (!didheal) { delete_list(newlist); return 0; // everyone was healthy; don't charge us } else { // Inform screen/view to print a message .. if (didheal == 1) sprintf(message, "Cleric healed 1 man!"); else sprintf(message, "Cleric healed %d men!", didheal); if (team_num == 0 || myguy) // home team screenp->do_notify(message, this); // Play sound ... if (on_screen()) screenp->soundp->play_sound(SOUND_HEAL); delete_list(newlist); newlist = NULL; } // end of did heal guys case } else // no friends, so don't charge us { delete_list(newlist); return 0; } if (newlist) delete_list(newlist); break; } // end of normal heal else // else do mystic mace { // First do legality checks: // Can't do more than 1/5 rounds if (busy) return 0; // Do we have the int? if (myguy && myguy->intelligence < 50) // need 50+ { if (user != -1) // only players get this myscreen->do_notify("50 Int required for Mystic Mace!", this); return 0; } if (myguy) myguy->total_shots +=1; // record that we fired/attacked // All okay, let's summon! newob = screenp->add_ob(ORDER_FX, FAMILY_MAGIC_SHIELD); if (!newob) // safety check return 0; newob->owner = this; newob->team_num = team_num; newob->ani_type = 1; // dummy, non-zero value // Specify settings based on our mana .. generic = stats->magicpoints - stats->special_cost[current_special]; generic /= 2; // get half our excess magic newob->lifetime = 100 + generic; newob->stats->hitpoints += generic / 2; newob->damage += generic / 4; // Remove those excess magic points :> stats->magicpoints -= generic; busy += 5; break; } // end of mystic mace case 2: // raise skeletons if (shifter_down) // turn undead, low level { if (busy) return 0; if (myguy && myguy->intelligence < 60) // check for minimum req. { if ( (team_num == 0 || myguy) && on_screen() ) screenp->do_notify("You need 60 Int to Turn Undead", this); busy +=5; return 0; } if ( (generic=turn_undead(4*stats->level, stats->level)) == -1 ) return 0; // failed to turn undead if (myguy && generic) { myguy->exp += (generic*3); // (stats->level/2)); if (team_num == 0 || myguy) { strcpy(message, myguy->name); sprintf(message, "%s turned %d undead.", myguy->name, generic); screenp->do_notify(message, this); } // end of notify visually } // Play sound ... if (on_screen()) screenp->soundp->play_sound(SOUND_HEAL); } // end of turn undead, low level else { newob = screenp->find_nearest_blood(this); if (newob) { targetx = newob->xpos; targety = newob->ypos; distance = (unsigned long) distance_to_ob(newob); //(targetx-xpos)*(targetx-xpos) + (targety-ypos)*(targety-ypos); if (screenp->query_passable(targetx, targety, newob) && distance < 60) { alive = do_summon(FAMILY_SKELETON, 125 + (stats->level*40) ); if (!alive) return 0; alive->team_num = team_num; alive->stats->level = random(stats->level) + 1; alive->set_difficulty((unsigned long) alive->stats->level); alive->setxy(newob->xpos, newob->ypos); alive->owner = this; //screenp->remove_fx_ob(newob); //screenp->remove_ob(newob, 0); newob->dead = 1; if (myguy) myguy->exp += 45; } // end passable check else return 0; } // end if-newob check else return 0; //end of raise skeletons } // end of the else-check break; case 3: // Raise ghosts .. if (shifter_down) // turn undead, high level { if (busy) return 0; if (myguy && myguy->intelligence < 60) // check for minimum req. { if ((team_num == 0 || myguy) && on_screen() ) screenp->do_notify("You need 60 Int to Turn Undead", this); busy +=5; return 0; } if ( (generic=turn_undead(4*stats->level, stats->level)) == -1 ) return 0; // failed to turn undead if (myguy && generic) { myguy->exp += (generic*3); // (stats->level/2)); if (team_num == 0 || myguy) { strcpy(message, myguy->name); sprintf(message, "%s turned %d undead.", myguy->name, generic); screenp->do_notify(message, this); } // end of notify visually } // Play sound ... if (on_screen()) screenp->soundp->play_sound(SOUND_HEAL); } // end of turn undead, high level else { newob = screenp->find_nearest_blood(this); if (newob) { targetx = newob->xpos; targety = newob->ypos; distance = (unsigned long) distance_to_ob(newob); //(targetx-xpos)*(targetx-xpos) + (targety-ypos)*(targety-ypos); if (screenp->query_passable(targetx, targety, newob) && distance < 30) { //alive = screenp->add_ob(ORDER_LIVING, FAMILY_SKELETON); alive = do_summon(FAMILY_GHOST, 150 + (stats->level*40) ); if (!alive) return 0; alive->stats->level = random(stats->level) + 1; alive->set_difficulty((unsigned long) alive->stats->level); alive->team_num = team_num; alive->setxy(newob->xpos, newob->ypos); alive->owner = this; //screenp->remove_fx_ob(newob); //screenp->remove_ob(newob, 0); newob->dead = 1; if (myguy) myguy->exp += 60; } // end of passable check else return 0; } // end of if-newob check else return 0; // end of raise ghosts } // end of else check break; case 4: // Resurrect our guys .. default: newob = screenp->find_nearest_blood(this); if (newob) { targetx = newob->xpos; targety = newob->ypos; distance = distance_to_ob(newob); //(targetx-xpos)*(targetx-xpos) + (targety-ypos)*(targety-ypos); if (screenp->query_passable(targetx, targety, newob) && distance < 30) { if ( is_friendly(newob) ) // normal ressurection { alive = screenp->add_ob(ORDER_LIVING, newob->stats->old_family); if (!alive) return 0; // failsafe newob->transfer_stats(alive); // restore our old values .. alive->stats->hitpoints = (alive->stats->max_hitpoints)/2; alive->team_num = newob->team_num; if (myguy) // take some EXP away as penalty if we're a player { if (myguy->exp >= (newob->stats->level*newob->stats->level*100) ) myguy->exp -= (newob->stats->level*newob->stats->level*100); else myguy->exp = 0; } } else // raise an opponent as undead { alive = do_summon(FAMILY_GHOST, 200); if (!alive) return 0; alive->team_num = team_num; alive->stats->level = random(stats->level) + 1; alive->set_difficulty((unsigned long) alive->stats->level); alive->owner = this; } alive->setxy(newob->xpos, newob->ypos); //screenp->remove_fx_ob(newob); //screenp->remove_ob(newob, 0); newob->dead = 1; if (myguy) myguy->exp += 90; } // end of passable else return 0; } // end of if newob else return 0; // end of ressurection break; } break; // end of cleric case FAMILY_MAGE: switch (current_special) { case 1: // Teleport if (ani_type == ANI_TELE_OUT || ani_type == ANI_TELE_IN) return 0; if (shifter_down) // leave/remove a marker { if (busy) return 0; if (myguy && (myguy->intelligence < 75) ) { if (user != -1) // we're a real player .. screenp->do_notify("Need 75 Int for Marker!", this); return 0; // so as not to charge player } // Remove a marker, if present newlist = screenp->oblist; generic = 0; // used to check progress while (newlist) { if (newlist->ob && newlist->ob->query_order() == ORDER_FX && newlist->ob->query_family() == FAMILY_MARKER && newlist->ob->owner == this && !newlist->ob->dead ) { newlist->ob->dead = 1; newlist->ob->death(); if ((team_num == 0 || myguy) && user!=-1) screenp->do_notify("(Old Marker Removed)", this); busy += 8; generic = 1; } newlist = newlist->next; if (generic) newlist = NULL; } generic = 0; // force new placement, for now if (!generic) // didn't remove a marker, so place one { newob = screenp->add_ob(ORDER_FX, FAMILY_MARKER); if (!newob) return 0; // failsafe newob->owner = this; newob->center_on(this); if (myguy) newob->lifetime = myguy->intelligence / 33; else newob->lifetime = (stats->level / 4) + 1; newob->ani_type = ANI_SPIN; // non-walking if ((team_num == 0 || myguy) && user != -1) { screenp->do_notify("Teleport Marker Placed", this); sprintf(message, "(%ld Uses)", newob->lifetime); screenp->do_notify(message, this); } busy +=8; // Take an extra cost for placing a marker generic = stats->magicpoints - stats->special_cost[current_special]; generic /= 2; // reduce our 'extra' by half stats->magicpoints -= generic; } } // end of put a marker else { if (on_screen()) screenp->soundp->play_sound(SOUND_TELEPORT); ani_type = ANI_TELE_OUT; cycle = 0; } break; case 2: tempx = lastx; // store our facing tempy = lasty; // Do we have extra magic points to spend? generic = stats->magicpoints - stats->special_cost[current_special]; if (generic > 0) { generic = generic / 15; // take 7% of remaining magic... stats->magicpoints -= generic; // and subtract this cost ... } else generic = 0; // Now face each direction and fire .. stats->magicpoints += (8*stats->weapon_cost); for (i=-1;i<2;i++) for (j=-1;j<2;j++) { if (i || j) { lastx = i; lasty = j; newob = fire(); if (newob) { newob->damage += generic; // bonus for extra mp newob->lineofsight += (generic/3); if (newob->lastx) newob->lastx /= abs(newob->lastx); if (newob->lasty) newob->lasty /= abs(newob->lasty); } // end got a valid weapon } // end checked for not center } // end did all 8 directions // Restore old facing lastx = tempx; lasty = tempy; break; case 3: // Freeze time if (team_num == 0 || myguy) // the player's team { screenp->enemy_freeze += 20 + 11*stats->level; set_palette(screenp->bluepalette); } else { generic = 5 + 2*stats->level; if (generic > 50) generic = 50; sprintf(message, "TIME IS FROZEN! (%d rounds)", generic); screenp->viewob[0]->set_display_text(message, 2); screenp->viewob[0]->redraw(); screenp->viewob[0]->refresh(); //screenp->buffer_to_screen(0, 0, 320, 200); newlist = screenp->find_friends_in_range( screenp->oblist, 30000, &howmany, this); here = newlist; while (here) { if (here->ob) here->ob->bonus_rounds += generic; here = here->next; } delete_list(newlist); newlist = NULL; } break; case 4: // Energy wave newob = fire(); if (!newob) return 0; // failed somehow? !?! alive = screenp->add_ob(ORDER_WEAPON, FAMILY_WAVE); alive->center_on(newob); alive->owner = this; alive->stats->level = stats->level; alive->lastx = newob->lastx; alive->lasty = newob->lasty; newob->dead = 1; break; case 5: default: // Burst enemies into flame .. newlist = screenp->find_foes_in_range(screenp->oblist, 80+2*stats->level, &howmany, this); if (!howmany) return 0; // didn't find any enemies.. here = newlist; generic = stats->magicpoints - stats->special_cost[5]; generic /= 2; generic /= howmany; // so do half magic, div enemies if (myguy) myguy->total_shots += howmany; busy += 5; while (here) { newob = screenp->add_ob(ORDER_FX, FAMILY_EXPLOSION); if (!newob) { delete_list(newlist); return 0; // failsafe } newob->owner = this; newob->team_num = team_num; newob->stats->level = stats->level; newob->damage = generic; newob->center_on(here->ob); if (on_screen()) screenp->soundp->play_sound(SOUND_EXPLODE); newob->ani_type = ANI_EXPLODE; newob->stats->set_bit_flags(BIT_MAGICAL, 1); newob->skip_exit = 100; // don't hurt caster stats->magicpoints -= generic; here = here->next; } delete_list(newlist); break; // end of burst enemies } break; // end of mage case FAMILY_ARCHMAGE: switch (current_special) { case 1: // Teleport if (ani_type == ANI_TELE_OUT || ani_type == ANI_TELE_IN) return 0; if (shifter_down) // leave/remove a marker { if (busy) return 0; if (myguy && (myguy->intelligence < 75) ) { screenp->do_notify("Need 75 Int for Marker!", this); return 0; // so as not to charge player } // Remove a marker, if present newlist = screenp->oblist; generic = 0; // used to check progress while (newlist) { if (newlist->ob && newlist->ob->query_order() == ORDER_FX && newlist->ob->query_family() == FAMILY_MARKER && newlist->ob->owner == this && !newlist->ob->dead ) { newlist->ob->dead = 1; newlist->ob->death(); if (team_num == 0 || myguy) screenp->do_notify("(Old Marker Removed)", this); busy += 8; generic = 1; } newlist = newlist->next; if (generic) newlist = NULL; } // end of cycle through object list // Now place a marker .. newob = screenp->add_ob(ORDER_FX, FAMILY_MARKER); if (!newob) return 0; // failsafe newob->owner = this; newob->center_on(this); if (myguy) newob->lifetime = myguy->intelligence / 33; else newob->lifetime = (stats->level / 4) + 1; newob->ani_type = 2; // non-walking if (team_num == 0 || myguy) { screenp->do_notify("Teleport Marker Placed", this); sprintf(message, "(%ld Uses)", newob->lifetime); screenp->do_notify(message, this); } busy +=8; // Take an extra cost for placing a marker generic = stats->magicpoints - stats->special_cost[current_special]; generic /= 2; // reduce our 'extra' by half stats->magicpoints -= generic; } // end of put a marker (shifter_down) else { if (on_screen()) screenp->soundp->play_sound(SOUND_TELEPORT); ani_type = ANI_TELE_OUT; cycle = 0; } break; // end of ArchMage's teleport case 2: // Burst enemies into flame, or chain lightning.. if (busy) return 0; if (shifter_down) { if (myguy) generic = 200+myguy->intelligence/2; // range to scan for enemies else generic = 200+stats->level*5; } else generic = 80; newlist = screenp->find_foes_in_range(screenp->oblist, generic+2*stats->level, &howmany, this); if (!howmany) return 0; // didn't find any enemies.. here = newlist; if (!shifter_down) // normal usage { generic = stats->magicpoints - stats->special_cost[2]; generic /= 2; generic /= howmany; // so do half magic, div enemies if (myguy) myguy->total_shots += howmany; busy += 5; while (here) { newob = screenp->add_ob(ORDER_FX, FAMILY_EXPLOSION); if (!newob) { delete_list(newlist); return 0; // failsafe } newob->owner = this; newob->team_num = team_num; newob->stats->level = stats->level; newob->stats->set_bit_flags(BIT_MAGICAL, 1); newob->damage = generic; newob->center_on(here->ob); if (on_screen()) screenp->soundp->play_sound(SOUND_EXPLODE); newob->ani_type = ANI_EXPLODE; newob->stats->set_bit_flags(BIT_MAGICAL, 1); newob->skip_exit = 100; // don't hurt caster stats->magicpoints -= generic; here = here->next; } delete_list(newlist); } // end of heartburst, standard case else // do chain-lightning { busy += 5; if (myguy) myguy->total_shots += 1; // so can get > 100% :) newob = screenp->add_ob(ORDER_FX, FAMILY_CHAIN); newob->center_on(this); newob->owner = this; newob->stats->level = stats->level; newob->team_num = team_num; // Use half our remaining magic .. generic = stats->magicpoints - stats->special_cost[2]; generic /= 2; stats->magicpoints -= generic; newob->damage = generic; generic = distance_to_ob_center(here->ob); newob->leader = here->ob; // first on the list .. while (here) // find closest of our foes in range { if (distance_to_ob_center(newob->leader) > distance_to_ob_center(here->ob) ) newob->leader = here->ob; here = here->next; } // Clean up memory by deleting list .. /* This breaks in stats; is it bad here too? if (newlist) { here = newlist->next; while (here) { delete newlist; newlist = here; here = here->next; } delete newlist; } // end of clean-up memory */ // this should work delete_list(newlist); //newob->ani_type = ANI_ATTACK; } // end of chain-lightning break; // end of burst enemies, chain lightning case 3: // Summoning .. real or illusion if (busy) return 0; if (shifter_down) // then we do true summoning .. { // Do we have the int? if (myguy && myguy->intelligence < 150) // need 150+ { if (user != -1) // only players get this myscreen->do_notify("150 Int required to Summon!", this); return 0; } // Take an extra 50% mana-cost generic = stats->magicpoints - stats->special_cost[3]; generic /= 2; stats->magicpoints -= generic; // First make the guy we'd summon, at least physically newob = screenp->add_ob(ORDER_LIVING, FAMILY_FIREELEMENTAL); if (!newob) return 0; // failsafe // We need to check for a space around the archmage... generic = 0; // this means we have or haven't found room for (i=-1; i <= 1; i++) for (j=-1; j <= 1; j++) { if ( (i==0 && j==0) || (generic) ) continue; if (screenp->query_passable(xpos+((newob->sizex+1)*i), ypos+((newob->sizey+1)*j), newob)) { // We've found a legal spot .. generic = 1; newob->setxy(xpos+((newob->sizex+1)*i), ypos+((newob->sizey+1)*j)); newob->stats->level = (stats->level+1)/2; newob->set_difficulty(newob->stats->level); newob->team_num = team_num; // set to our team newob->owner = this; // we're owned! newob->lifetime = 200 + 60*stats->level; } // end of successfully put summoned creature } // end of I and J loops if (!generic) // we never found a legal spot { newob->dead = 1; return 0; } busy += 15; // takes lots of time :) } // end of shifter_down true summoning else // standard, illusion-only { // Determine what type of thing to summon image of generic = stats->magicpoints - stats->special_cost[3]; if (generic < 100) // lowest type person = FAMILY_ELF; else if (generic < 250) { switch (random(3)) { case 0: person = FAMILY_ELF; break; case 1: person = FAMILY_SOLDIER; break; case 2: person = FAMILY_ARCHER; break; default: person = FAMILY_SOLDIER; break; } } else if (generic < 500) { switch (random(5)) { case 0: person = FAMILY_ELF; break; case 1: person = FAMILY_SOLDIER; break; case 2: person = FAMILY_ARCHER; break; case 3: person = FAMILY_ORC; break; case 4: person = FAMILY_SKELETON; break; default: person = FAMILY_ARCHER; break; } } else if (generic < 1000) { switch (random(7)) { case 0: person = FAMILY_ELF; break; case 1: person = FAMILY_SOLDIER; break; case 2: person = FAMILY_ARCHER; break; case 3: person = FAMILY_ORC; break; case 4: person = FAMILY_SKELETON; break; case 5: person = FAMILY_DRUID; break; case 6: person = FAMILY_CLERIC; break; default: person = FAMILY_ARCHER; break; } } else // our maximum possible, insert before if needed { switch (random(9)) { case 0: person = FAMILY_ELF; break; case 1: person = FAMILY_SOLDIER; break; case 2: person = FAMILY_ARCHER; break; case 3: person = FAMILY_ORC; break; case 4: person = FAMILY_SKELETON; break; case 5: person = FAMILY_DRUID; break; case 6: person = FAMILY_CLERIC; break; case 7: person = FAMILY_FIREELEMENTAL; break; case 8: person = FAMILY_BIG_ORC; break; default: person = FAMILY_ARCHER; break; } } // Now make the guy we'd summon, at least physically newob = screenp->add_ob(ORDER_LIVING, person); if (!newob) return 0; // failsafe // We need to check for a space around the archmage... generic = 0; // this means we have or haven't found room for (i=-1; i <= 1; i++) for (j=-1; j <= 1; j++) { if ( (i==0 && j==0) || (generic) ) continue; if (screenp->query_passable(xpos+((newob->sizex+1)*i), ypos+((newob->sizey+1)*j), newob)) { // We've found a legal spot .. generic = 1; newob->setxy(xpos+((newob->sizex+1)*i), ypos+((newob->sizey+1)*j)); newob->stats->level = (stats->level+2)/3; newob->set_difficulty(newob->stats->level); newob->team_num = team_num; // set to our team newob->owner = this; // we're owned! newob->lifetime = 100 + 20*stats->level; //newob->stats->armor = -(newob->stats->max_hitpoints*10); newob->stats->max_hitpoints = 1; newob->stats->hitpoints = 0; newob->stats->armor = 0; newob->foe = foe; // just to help out .. newob->stats->set_bit_flags(BIT_MAGICAL, 1); // we're magical strcpy(newob->stats->name, "Phantom"); } // end of successfully put summoned creature-image } // end of I and J loops if (!generic) // we never found a legal spot { newob->dead = 1; return 0; } busy += 15; // takes lots of time :) } // end of summon illusion break; // end of summoning/illusion cases case 4: // Mind-control enemies if (busy) return 0; newlist = screenp->find_foes_in_range(screenp->oblist, 80+4*stats->level, &howmany, this); if (howmany < 1) return 0; // noone to influence here = newlist; didheal = 0; // howmany actually done yet? generic2 = stats->magicpoints - stats->special_cost[current_special] + 10; while (here && (generic2 >= 10) ) { if ( (here->ob->real_team_num == 255) && // never been charmed (here->ob->query_order() == ORDER_LIVING) && // alive (here->ob->charm_left <= 10) // not too charmed ) { generic2 -= 10; // count cost for additional guy generic = stats->level - here->ob->stats->level; if (generic < 0 || (!random(20)) ) // trying to control a higher-level { here->ob->real_team_num = here->ob->team_num; here->ob->team_num = random(8); here->ob->charm_left = 25 + random(generic*20); } else { here->ob->real_team_num = here->ob->team_num; here->ob->team_num = team_num; here->ob->foe = NULL; // allow choice of new foe here->ob->charm_left = 25 + random(generic*20); } didheal++; } here = here->next; } delete_list(newlist); if (!didheal) // didn't actually get anyone? return 0; // Notify screen of our action if (strlen(stats->name)) // do we have an NPC name? strcpy(message, stats->name); else if (myguy && strlen(myguy->name) ) strcpy(message, myguy->name); else strcpy(message, "ArchMage"); sprintf(tempstr, "%s has controlled %d men", message, didheal); screenp->do_notify(tempstr, this); generic2 = stats->magicpoints - stats->special_cost[current_special]; if (generic2 > 0) // sap our extra based on how many guys { while ( (didheal > 0) && (generic2 >= 10) ) { if (generic2 > 10) // 10 is cost of each additional guy generic2 -= 10; didheal--; } } // end of extra-cost sapping busy += 10; // takes a while break; // end of Mind control default: break; } break; // end of ArchMage case FAMILY_FIREELEMENTAL: switch (current_special) { case 1: // lots o' fireballs case 2: case 3: case 4: default: tempx = lastx; // store our facing tempy = lasty; // Now face each direction and fire .. stats->magicpoints += (8*stats->weapon_cost); for (i=-1;i<2;i++) for (j=-1;j<2;j++) { if (i || j) { lastx = i; lasty = j; fire(); } } // Restore old facing lastx = tempx; lasty = tempy; break; } break; // end of fire elemental case FAMILY_SMALL_SLIME: // grow .. case FAMILY_MEDIUM_SLIME: if (spaces_clear() > 7) // room to grow? { if (query_family() == FAMILY_SMALL_SLIME) transform_to(ORDER_LIVING, FAMILY_MEDIUM_SLIME); else transform_to(ORDER_LIVING, FAMILY_SLIME); } else { stats->set_command(COMMAND_WALK,10,random(3)-1,random(3)-1); return 0; } break; case FAMILY_SLIME: // Big slime splits to two small slimes ani_type = ANI_SLIME_SPLIT; cycle = 0; break; case FAMILY_GHOST: // do nifty scare thing newob = screenp->add_ob(ORDER_FX, FAMILY_GHOST_SCARE); //,1 == underneath newob->ani_type = ANI_SCARE; newob->setxy(xpos+sizex/2 - newob->sizex/2, ypos+sizey/2 - newob->sizey/2); newob->owner = this; newob->stats->level = stats->level; newob->team_num = team_num; // so we scare OTHER teams // Actual scare effect done in scare's "death" in effect break; case FAMILY_THIEF: switch (current_special) { case 1: // drop a bomb, unregistered newob = screenp->add_ob(ORDER_FX, FAMILY_BOMB, 1); // 1 == underneath newob->ani_type = ANI_BOMB; if (myguy) myguy->total_shots += 1; newob->damage = (stats->level+1)*15; newob->setxy(xpos+sizex/2 - newob->sizex/2, ypos+sizey/2 - newob->sizey/2); newob->owner = this; // Run away if we're AI person = 0; for (i=0; i < screenp->numviews; i++) if (screenp->viewob[i]->control == this) person = 1; if (!person) { tempx = random(3)-1; tempy = random(3)-1; if ( (tempx==0) && (tempy==0) ) tempx = 1; stats->force_command(COMMAND_WALK, 20, tempx,tempy); } break; case 2: // thief cloaking ability, Registered invisibility_left += 20 + ((random(20))*stats->level); break; case 3: // thief Taunt (draw enemies), Registered if (!shifter_down) // normal taunt { if (busy) return 0; newlist = screenp->find_foes_in_range(screenp->oblist, 80+4*stats->level, &howmany, this); here = newlist; while (here) { if (here->ob && (random(stats->level) >= random(here->ob->stats->level)) ) { // Set our enemy's foe to us.. here->ob->foe = this; here->ob->leader = this; // a hack, yeah if (here->ob->query_act_type() != ACT_CONTROL) here->ob->stats->force_command(COMMAND_FOLLOW, 10+random(stats->level), 0, 0); } here = here->next; } delete_list(newlist); if (myguy) strcpy(message, myguy->name); else if ( strlen(stats->name) ) strcpy(message, stats->name); else strcpy(message, "THIEF"); strcat(message, ": 'Nyah Nyah!'"); screenp->do_notify(message, this); busy += 2; break; // end of taunt } else // charm opponent { if (busy) return 0; newlist = screenp->find_foes_in_range(screenp->oblist, 16+4*stats->level, &howmany, this); if (howmany < 1) return 0; // noone to influence here = newlist; didheal = 0; // howmany actually done yet? while (here && !didheal) { if ( (here->ob->real_team_num == 255) && // never been charmed (here->ob->query_order() == ORDER_LIVING) && // alive 1 // (here->ob->charm_left <= 10) // not too charmed ) { generic = stats->level - here->ob->stats->level; if (generic < 0 || (!random(20)) ) // trying to control a higher-level { // Enemy gets free attack .. here->ob->foe = this; here->ob->attack(this); generic2 = 1; } else { here->ob->real_team_num = here->ob->team_num; here->ob->team_num = team_num; if (foe == here->ob) here->ob->foe = NULL; else here->ob->foe = foe; here->ob->charm_left = 75 + generic*25; generic2 = 0; } didheal++; } // end of if-valid-target here = here->next; } // end of until-got-target loop delete_list(newlist); if (!didheal) return 0; // Notify screen of our action if (strlen(stats->name)) // do we have an NPC name? strcpy(message, stats->name); else if (myguy && strlen(myguy->name) ) strcpy(message, myguy->name); else strcpy(message, "Thief"); if (generic2) // then we actually failed to charm sprintf(tempstr, "%s failed to charm!", message); else sprintf(tempstr, "%s charmed an opponent!", message); screenp->do_notify(tempstr, this); busy += 10; // takes a while break; // end of Charm Opponent } case 4: // throw poison cloud default: if (busy) return 0; newob = screenp->add_ob(ORDER_FX, FAMILY_CLOUD); if (!newob) return 0; // failsafe busy += 5; newob->ignore = 1; newob->lifetime = 40 + 3*stats->level; newob->center_on(this); newob->invisibility_left = 10; newob->ani_type = ANI_SPIN; // non-walking newob->team_num = team_num; newob->stats->level = stats->level; newob->damage = stats->level; newob->owner = this; break; } break; case FAMILY_ELF: switch(current_special) { case 1: // some rocks (normal) stats->magicpoints += (2*stats->weapon_cost); fire(); fire(); break; case 2: // more rocks, and bouncing stats->magicpoints += (3*stats->weapon_cost); for (i=0; i < 2; i++) { fireob = (weap*) fire(); if (!fireob) // failsafe return 0; fireob->lineofsight *= 3; // we get 50% longer, too! fireob->lineofsight /= 2; fireob->do_bounce = 1; } break; case 3: stats->magicpoints += (4*stats->weapon_cost); for (i=0; i < 3; i++) { fireob = (weap*) fire(); if (!fireob) // failsafe return 0; fireob->lineofsight *= 2; // get double distance fireob->do_bounce = 1; } break; case 4: default: stats->magicpoints += (5*stats->weapon_cost); for (i=0; i < 4; i++) { fireob = (weap*) fire(); if (!fireob) // failsafe return 0; fireob->lineofsight *= 5; // we get 150% longer, too! fireob->lineofsight /= 2; fireob->do_bounce = 1; } break; } break; case FAMILY_DRUID: switch (current_special) { case 1: // plant tree if (busy) return 0; stats->magicpoints += stats->weapon_cost; newob = fire(); if (!newob) return 0; busy += (fire_frequency * 2); alive = screenp->add_ob(ORDER_WEAPON,FAMILY_TREE); alive->setxy(newob->xpos,newob->ypos); alive->team_num = team_num; alive->ani_type = ANI_GROW; alive->owner = this; newob->dead = 1; break; case 2: // summon faerie if (busy) return 0; stats->magicpoints += stats->weapon_cost; newob = fire(); if (!newob) return 0; alive = screenp->add_ob(ORDER_LIVING, FAMILY_FAERIE); alive->setxy(newob->xpos, newob->ypos); alive->team_num = team_num; alive->owner = this; alive->lifetime = 50 + stats->level*(40); newob->dead = 1; if (!screenp->query_passable(alive->xpos, alive->ypos, alive)) { alive->dead = 1; return 0; } busy += (fire_frequency * 3); break; case 3: // reveal items if (busy) return 0; view_all += stats->level*10; busy += (fire_frequency * 4); break; case 4: // circle of protection default: if (busy) return 0; newlist = screenp->find_friends_in_range(screenp->oblist, 60, &howmany, this); didheal = 0; if (howmany > 1) // some friends here .. { here = newlist; //printf("Found %d friends\n", howmany-1); while (here) { newob = here->ob; if (newob != this) // not for ourselves { // First see if this person already has protection (slow) list2 = screenp->oblist; tempwalk = NULL; while (list2 && !tempwalk) { if (list2->ob && list2->ob->owner == newob && list2->ob->query_order() == ORDER_WEAPON && list2->ob->query_family() == FAMILY_CIRCLE_PROTECTION ) // found a circle already on newob ... tempwalk = list2->ob; list2 = list2->next; } if (!tempwalk) // target wasn't protected yet { alive = screenp->add_ob(ORDER_WEAPON, FAMILY_CIRCLE_PROTECTION); if (!alive) // failed somehow { delete_list(newlist); return 0; } alive->owner = newob; alive->center_on(newob); alive->team_num = newob->team_num; alive->stats->level = newob->stats->level; if (myguy) myguy->exp += stats->level; didheal++; } // end of target wasn't protected else { alive = screenp->add_ob(ORDER_WEAPON, FAMILY_CIRCLE_PROTECTION); if (!alive) // failed somehow { delete_list(newlist); return 0; } tempwalk->stats->hitpoints += alive->stats->hitpoints; alive->dead = 1; if (myguy) myguy->exp += stats->level; didheal++; } // end of target WAS protected } // end of did one guy here = here->next; } // end of cycling through guys if (!didheal) { delete_list(newlist); return 0; // everyone was okay; don't charge us } else { // Inform screen/view to print a message .. if (didheal == 1) sprintf(message, "Druid protected 1 man!"); else sprintf(message, "Druid protected %d men!", didheal); if (team_num == 0 || myguy) // home team screenp->do_notify(message, this); // Play sound ... if (on_screen()) screenp->soundp->play_sound(SOUND_HEAL); delete_list(newlist); newlist = NULL; } // end of did protect guys case } // end of checking for friends else // no friends, so don't charge us { if (newlist) delete_list(newlist); return 0; } if (newlist) delete_list(newlist); break; // end of druid's specials .. } // end of switch on druid case break; case FAMILY_ORC: // registered monster switch (current_special) { case 1: // yell and 'freeze' foes if (busy) return 0; busy += 2; newlist = screenp->find_foes_in_range(screenp->oblist, 160+(20*stats->level), &howmany, this); here = newlist; while (here) { if (here->ob) { if (here->ob->myguy) tempx = here->ob->myguy->constitution; else tempx = here->ob->stats->hitpoints / 30; tempy = 10 + random(stats->level*10) - random(tempx*10); if (tempy < 0) tempy = 0; here->ob->stats->frozen_delay += tempy; } here = here->next; } delete_list(newlist); if (on_screen()) screenp->soundp->play_sound(SOUND_ROAR); break; case 2: // eat corpse for health case 3: case 4: default: if (stats->hitpoints >= stats->max_hitpoints) return 0; // can't eat if we're 'full' newob = screenp->find_nearest_blood(this); if (!newob) // no blood, so do nothing return 0; distance = (unsigned long) distance_to_ob_center(newob); if (distance > 24) // must be close enough return 0; stats->hitpoints += newob->stats->level*5; // Print the eating notice if (myguy) { myguy->exp += newob->stats->level*5; strcpy(message, myguy->name); } else if ( strlen(stats->name) ) strcpy(message, stats->name); else strcpy(message, "Orc"); strcat(message, " ate a corpse."); screenp->do_notify(message, this); if (stats->hitpoints > stats->max_hitpoints) stats->hitpoints = stats->max_hitpoints; newob->dead = 1; newob->death(); break; // end of eat corpse } // end of orc case break; case FAMILY_SKELETON: switch (current_special) { case 1: // Tunnel case 2: case 3: case 4: default: if (ani_type == ANI_TELE_OUT || ani_type == ANI_TELE_IN) return 0; ani_type = ANI_TELE_OUT; cycle = 0; break; break; // end of tunnel case } // end of skeleton case break; // end of Skeleton case FAMILY_BARBARIAN: switch (current_special) { case 1: // Hurl Boulder case 2: // Exploding Boulder case 3: case 4: if (busy) return 0; newob = fire(); if (!newob) return 0; // failed somehow? !?! alive = screenp->add_ob(ORDER_WEAPON, FAMILY_BOULDER); alive->center_on(newob); alive->owner = this; alive->stats->level = stats->level; alive->lastx = newob->lastx; alive->lasty = newob->lasty; // Set our boulder's speed and extra damage .. if (myguy) { alive->stepsize = myguy->strength / 7; alive->damage += myguy->strength / 5; } else { alive->stepsize = stats->level * 2; alive->damage += stats->level; } if (alive->stepsize < 1) alive->stepsize = 1; if (alive->stepsize > 15) alive->stepsize = 15; if (alive->lasty > 0) alive->lasty = alive->stepsize; else if (alive->lasty < 0) alive->lasty = -(alive->stepsize); if (alive->lastx > 0) alive->lastx = alive->stepsize; else if (alive->lastx < 0) alive->lastx = -(alive->stepsize); // If we're on 'exploding boulder,' then // make it explode on impact. if (current_special == 2) alive->skip_exit = 5000; // signify exploding else alive->skip_exit = 0; newob->dead = 1; busy += 1 + current_special * 5; break; // end of hurl boulder } // end of Barbarian break; } // end of family switch stats->magicpoints -= stats->special_cost[current_special]; return 0; } short walker::teleport() { short newx,newy; oblink *newlist; long distance; // First check to see if we have a marker to go to // NOTE: it must be a bit away from us .. newlist = screenp->oblist; while (newlist) { if (newlist->ob && newlist->ob->query_order() == ORDER_FX && newlist->ob->query_family() == FAMILY_MARKER && newlist->ob->owner == this && !newlist->ob->dead ) { // Found our marker! if (screenp->query_passable(newlist->ob->xpos, newlist->ob->ypos, this) && (distance = distance_to_ob(newlist->ob) > 64) ) { center_on(newlist->ob); newlist->ob->lifetime--; if (newlist->ob->lifetime < 1) { newlist->ob->dead = 1; newlist->ob->death(); } return 1; } // end of successful transport else // blocked somehow? { if (user != -1 && (distance > 64) ) // only tell players screenp->do_notify("Marker is Blocked!", this); } } newlist = newlist->next; } // end of checking for marker (we failed) newx = random(screenp->maxx)*GRID_SIZE; newy = random(screenp->maxy)*GRID_SIZE; while(!screenp->query_passable(newx, newy, this)) { newx = random(screenp->maxx)*GRID_SIZE; newy = random(screenp->maxy)*GRID_SIZE; } setxy(newx,newy); return 1; } short walker::teleport_ranged(long range) { short newx,newy; short keep_going = 200; // maxtries newx = random(2*range) - range + xpos; newy = random(2*range) - range + ypos; while(!screenp->query_passable(newx, newy, this) && keep_going) { newx = random(2*range) - range + xpos; newy = random(2*range) - range + ypos; keep_going--; } if (keep_going) { setxy(newx,newy); return 1; } return 0; // failed to find safe spot } // Turns undead; ie, skeleton or ghost, within range // Returns the number of dead destroyed long walker::turn_undead(long range, long power) { oblink *deadlist; oblink *here; long killed = 0; short targets; deadlist = screenp->find_foes_in_range(screenp->oblist, range, &targets, this); if (!targets) return -1; here = deadlist; while (here) { if (here->ob && ( (here->ob->query_family() == FAMILY_SKELETON) || (here->ob->query_family() == FAMILY_GHOST) ) ) // end of if-check { if (random(range*40) > random(here->ob->stats->level*10) ) { here->ob->dead = 1; here->ob->stats->hitpoints = 0; //here->ob->death(); attack(here->ob); // to generate bloodspot, etc. killed++; } } here = here->next; } delete_list(deadlist); return killed; } // ******************************************* // // MONSTER intellIGENCE ROUTINES // // ******************************************* // Basically, we check a direction for foes. // If we find one, we init_fire. If not, // we do nothing. init_fire will take care of // turning us if we need it. short walker::fire_check(short xdelta, short ydelta) { walker *weapon = NULL; // short newx=0, newy=0; short i, loops; short xdir = 0; short ydir = 0; long distance; short targetdir; // Allow generators to 'always' succeed if (order == ORDER_GENERATOR) return 1; // Make sure we can fire if ( (order==ORDER_LIVING) && (family==FAMILY_SOLDIER) ) if (weapons_left < 1) return 0; weapon = create_weapon(); if (!weapon) return 0; set_weapon_heading(weapon); // set lastx, lasty based on our facing... weapon->collide_ob = NULL; // Based on facing, we alter the weapon's proposed // size so the collision check is fooled shorto checking // a long strip equal to the lineofsight times the size // of the weapon. if (!foe) // nobody to fire at? { //printf("fire check, no foe.\n"); //this does happen! but it appears harmless return 0; } if (stats->query_bit_flags(BIT_NO_RANGED)) { weapon->dead=1; return 0; } if (stats->weapon_cost > stats->magicpoints) { weapon->dead = 1; return 0; } distance = distance_to_ob(foe); if (distance > (long) ( (long) weapon->stepsize * (long) weapon->lineofsight) ) { weapon->dead = 1; return 0; } targetdir = facing(xdelta,ydelta); if (targetdir != curdir) { // turn(targetdir); weapon->dead = 1; return 0; } if (xdelta != 0) xdir = xdelta / abs(xdelta); if (ydelta != 0) ydir = ydelta / abs(ydelta); /* // why are we assuming walls don't matter in these two cases? if (!xdelta || !ydelta) // aligned on a major axis { weapon->dead = 1; return 1; } if ( abs( abs(xdelta) - abs(ydelta) ) < 3) { weapon->dead = 1; return 1; } else { weapon->dead = 1; // return 0; } */ // Run weapon through where it would go if all went well .. for (i=0; i < weapon->lineofsight; i++) { weapon->setxy(weapon->xpos + i*weapon->lastx, weapon->ypos + i*weapon->lasty); if ( !screenp->query_grid_passable(weapon->xpos, weapon->ypos, weapon) ) { // we hit a wall, so fail weapon->dead = 1; return 0; } if ( !screenp->query_object_passable(weapon->xpos, weapon->ypos, weapon) ) { // we hit an enemy, so good! weapon->dead = 1; return 1; } } // By this point, we should have won or lost .. fail if we went our // range and didn't hit anyone .. weapon->dead = 1; return 0; // Determine # of loops to look for guy if ( abs(xdelta) > abs(ydelta) ) loops = abs(xdelta); else loops = abs(ydelta); // * 16 is to match with grid coords for (i=0; i <= loops; i+=8) // half a grid square if ( !screenp->query_grid_passable(xpos+i*xdir, ypos+i*ydir, weapon) ) { weapon->dead = 1; //foe = NULL; // can't hit this guy //stats->try_command(COMMAND_RANDOM_WALK, random(8)); return 0; } weapon->dead = 1; // We have a good chance of hitting, so .. return 1; } /**************************************************** * * Act routines (static) * ****************************************************/ short walker::act_generate() { if ( screenp->numobs < MAXOBS && (random(stats->level*3) > (random(300+(screenp->numobs*8)) ) ) ) { lastx = 1-random(3); lasty = 1-random(3); if (!lastx && !lasty) lastx = 1; init_fire(lastx, lasty); // lastx = 0; // lasty = 0; stats->hitpoints++; if (stats->hitpoints > stats->max_hitpoints) stats->hitpoints--; } return 1; } short walker::act_fire() { if (!(lineofsight--)) // this is the range of the weapon { dead = 1; death(); } else if (!walk() || stats->query_bit_flags(BIT_NO_COLLIDE)) { // Hit the collide_ob; if (collide_ob && !collide_ob->dead) { attack(collide_ob); } if (!stats->query_bit_flags(BIT_IMMORTAL)) { dead = 1; death(); } } return 1; } short walker::act_guard() { // Check all directions for foes // if we find one, fire // if (fire_check(lastx, lasty) || // fire_check(lasty, lastx) || // fire_check(-lasty, -lastx) || // fire_check(-lastx, -lasty)) foe = screenp->find_near_foe(this); if (foe) { curdir = (char) facing(foe->xpos - xpos, foe->ypos-ypos); stats->try_command(COMMAND_FIRE,random(30)); return 1; } else return 0; } short walker::act_random() { short newx, newy; short xdist, ydist; // Specially put in to attempt to make enemy harder //if (random(sizex/GRID_SIZE)) return 0; // Find our foe if (!random(70) || (!foe)) foe = screenp->find_far_foe(this); if (!foe) return stats->try_command(COMMAND_RANDOM_WALK,20); xdist = foe->xpos - xpos; ydist = foe->ypos - ypos; // If foe is in firing range, turn and fire if (abs(xdist) < lineofsight*GRID_SIZE && abs(ydist) < lineofsight*GRID_SIZE) { if (fire_check(xdist, ydist)) { init_fire(xdist, ydist); stats->set_command(COMMAND_FIRE, random(24), xdist, ydist); return 1; } else // Nearest foe is blocked //foe = NULL; turn(facing(xdist,ydist)); } // Otherwise, try to walk toward foe newx = 0; newy = 0; if (foe) { newx = xdist; // total horizontal distance.. if (newx) // If it's not 0, then get newx = newx / abs(newx); // the normal of it.. newy = ydist; if (newy) newy = newy / abs(newy); } // end of if we had a foe .. else { while ( !newx && !newy) { newx = (1-random(3)); // Walk in some random direction newy = (1-random(3)); // other than 0,0 :) } } // If blocked collide_ob = NULL; // We can slide now, so always just walkstep, NOT using // stepsize .. return walkstep(newx, newy); // return 1; } // Returns the spaces 'clear' around us, out of a maximum // of eight .. short walker::spaces_clear() { short count = 0; short i, j; for (i=-1; i < 2; i++) for (j=-1; j < 2; j++) if (i || j) // don't check our own location if (screenp->query_passable(xpos+(i*sizex), ypos+(j*sizey), this) ) count++; return count; } void walker::transfer_stats(walker *newob) { guy *newguy; short i; // First do the 'stats' stuff .. newob->stats->hitpoints = stats->hitpoints; newob->stats->max_hitpoints = stats->max_hitpoints; newob->stats->heal_per_round = stats->heal_per_round; newob->stats->max_heal_delay = stats->max_heal_delay; // Magic.. newob->stats->magicpoints = stats->magicpoints; newob->stats->max_magicpoints = stats->max_magicpoints; newob->stats->magic_per_round = stats->magic_per_round/2; newob->stats->max_magic_delay = stats->max_magic_delay; newob->stats->level = stats->level; newob->stats->frozen_delay = stats->frozen_delay; for (i=0; i < 5; i++) newob->stats->special_cost[i] = stats->special_cost[i]; newob->stats->weapon_cost = stats->weapon_cost; newob->stats->bit_flags = stats->bit_flags; newob->stats->delete_me = stats->delete_me; // Do we have a 'guy' ? if (myguy) { newguy = new guy(); strcpy(newguy->name, myguy->name); newguy->strength = myguy->strength; newguy->constitution = myguy->constitution; newguy->dexterity = myguy->dexterity; newguy->intelligence = myguy->intelligence; newguy->level = myguy->level; newguy->armor = myguy->armor; newguy->exp = myguy->exp; // 'Kill-stats' newguy->kills = myguy->kills; newguy->level_kills = myguy->level_kills; newguy->total_damage = myguy->total_damage; newguy->total_hits = myguy->total_hits; newguy->total_shots = myguy->total_shots; newguy->next = myguy->next; newob->myguy = newguy; } } // change picture, etc. but NOT stats (use transfer_stats for that) void walker::transform_to(char whatorder, char whatfamily) { unsigned char * data; short xcenter, ycenter; short tempxpos, tempypos; short reset = 0; short tempact; // First remove us from the collision table.. screenp->myobmap->remove (this); if (order == whatorder) // same object type { reset = 1; tempact = query_act_type(); } // Reset bit flags //stats->set_bit_flags(BIT_ANIMATE, 0); //stats->set_bit_flags(BIT_FLYING, 0); //stats->set_bit_flags(BIT_NO_RANGED, 0); // Do this faster, for ALL flags: stats->bit_flags = 0; // Do this before resetting graphic so illegal // family values don't try to set graphics. // order and family are only set if legal screenp->set_walker(this, whatorder, whatfamily); // Reset the graphics data = screenp->myloader->graphics[PIX(order, family)]; facings = data+3; bmp = (unsigned char *)(data+3); frames = data[0]; frame = 0; cycle = 0; // Deal with resizing and centering .. xcenter = xpos + sizex/2; ycenter = ypos + sizey/2; sizex = data[1]; sizey = data[2]; size = sizex*sizey; tempxpos = xcenter - sizex/2; tempypos = ycenter - sizey/2; if (reset) set_act_type(tempact); setxy(tempxpos, tempypos); // automatically re-adds us to the list .. // set_frame(ani[curdir+ani_type*NUM_FACINGS][cycle]); // Don't manually set the frame here -- it can break circles // of protection, etc., which are special cases .. instead: set_frame(0); animate(); } // death is called when an object dies (or weapon destructed, etc.) // for special effects .. short walker::death() { // Note that the 'dead' variable should ALREADY be set by the // time this function is called, so that we can easily reverse // the decision :) walker *newob = NULL; long i; if (death_called) return 0; death_called = 1; if (myguy) // were we a real character? Then make a heart .. { newob = screenp->add_ob(ORDER_TREASURE, FAMILY_LIFE_GEM, 1); newob->stats->hitpoints = myguy->query_heart_value(); newob->stats->hitpoints *= 3; newob->stats->hitpoints /= 8; newob->team_num = team_num; // note: this is 75%, divided by 2, since score is doubled // at end of level newob->center_on(this); } switch (order) { case ORDER_LIVING: if ( (team_num == 0 || myguy) // our team && (screenp->scenario_type & SCEN_TYPE_SAVE_ALL) && (strlen(stats->name)) // we were named ) return screenp->endgame(SCEN_TYPE_SAVE_ALL); // failed switch (family) { case FAMILY_FIREELEMENTAL: // make us explode dead = 0; stats->magicpoints += stats->special_cost[1]; special(); dead = 1; break; case FAMILY_SLIME: // shrink to medium .. dead = 1; //transform_to(ORDER_LIVING, FAMILY_MEDIUM_SLIME); newob = screenp->add_ob(ORDER_LIVING, FAMILY_MEDIUM_SLIME); newob->team_num = team_num; newob->stats->level = stats->level; newob->set_difficulty(stats->level); newob->foe = foe; newob->leader = leader; if (strlen(stats->name)) strcpy(stats->name, newob->stats->name); if (myguy) { newob->myguy = myguy; myguy = NULL; } newob->center_on(this); stats->hitpoints = stats->max_hitpoints; break; case FAMILY_MEDIUM_SLIME: // shrink to small .. dead = 1; //transform_to(ORDER_LIVING, FAMILY_SMALL_SLIME); newob = screenp->add_ob(ORDER_LIVING, FAMILY_SMALL_SLIME); newob->team_num = team_num; newob->stats->level = stats->level; newob->set_difficulty(stats->level); newob->foe = foe; newob->leader = leader; if (strlen(stats->name)) strcpy(stats->name, newob->stats->name); if (myguy) { newob->myguy = myguy; myguy = NULL; } newob->center_on(this); stats->hitpoints = stats->max_hitpoints; break; case FAMILY_GHOST: // Undead don't leave bloodspots .. case FAMILY_SKELETON: case FAMILY_TOWER1: // neither do towers break; default: generate_bloodspot(); break; } // end of family switch break; // end of order livings case case ORDER_GENERATOR: // go up in flames :> for (i=0; i < 4; i++) { newob = screenp->add_ob(ORDER_FX, FAMILY_EXPLOSION, 1); if (!newob) // failsafe break; newob->team_num = team_num; newob->stats->level = stats->level; newob->ani_type = ANI_EXPLODE; newob->setxy(xpos+random(sizex-8)+4, ypos+4+random(sizey-8) ); newob->damage = stats->level*2; newob->set_frame(random(3)); if (on_screen()) screenp->soundp->play_sound(SOUND_EXPLODE); } break; case ORDER_FX: //case ORDER_TREASURE: printf("Effect dying in walker?\n"); break; // end of effect object case default: break; } // end of switching orders return 1; } // Generates bloodspot for desired walker... void walker::generate_bloodspot() { walker *bloodstain; //char *data; // Make permanent stain: dead = 1; // just in case .. bloodstain = screenp->add_fx_ob(ORDER_TREASURE, FAMILY_STAIN); bloodstain->ignore = 1; transfer_stats(bloodstain); bloodstain->order = ORDER_TREASURE; bloodstain->family = FAMILY_STAIN; bloodstain->stats->old_order = order; bloodstain->stats->old_family= family; bloodstain->team_num = team_num; bloodstain->dead = 0; bloodstain->setxy(xpos, ypos); //data = screenp->myloader->graphics[PIX(ORDER_TREASURE, FAMILY_STAIN)]; // We can't select other 'bloodspot' frames, because set_frame // appears to check the order and family and reset our picture // to a living guy .. we need to find a way around this .. bloodstain->set_frame(random(4)); // has no effect yet .. bloodstain->ani_type = ANI_WALK; //bloodstain->bmp = (char *) (data+3); // our image } short walker::eat_me(walker * eater) { if (eater) printf("EATING A NON-TREASURE!\n"); return 0; } void walker::set_direct_frame(short whichframe) { unsigned char * data; frame =whichframe; data = screenp->myloader->graphics[PIX(order, family)]; bmp = (unsigned char *) (data+3 + frame*size); } walker * walker::do_summon(char whatfamily, unsigned short lifetime) { if (whatfamily || lifetime) printf("Should not be hitting walker::do_summon!\n"); return NULL; } short walker::check_special() { printf("Should not be hitting walker::check_special\n"); return 0; } // Center us on target walker void walker::center_on(walker *target) { short newx, newy; // First get the center of our target .. newx = target->xpos + target->sizex/2; newy = target->ypos + target->sizey/2; // Now adjust for our position .. newx -= sizex/2; newy -= sizey/2; // Now set our position .. setxy(newx, newy); } void walker::set_difficulty(unsigned long whatlevel) { unsigned long temp, dif1; dif1 = difficulty_level[current_difficulty]; switch (order) { case ORDER_GENERATOR: temp = 100*whatlevel; temp = (temp * dif1) / 100; stats->hitpoints = temp; break; default: // adjust standard settings for the rest .. if (team_num != 0) // do all EXCEPT player characters { stats->max_hitpoints = (stats->max_hitpoints*dif1) / 100; stats->max_magicpoints = (stats->max_magicpoints*dif1) / 100; damage = (damage * dif1) / 100; } break; } return; } long walker::distance_to_ob(walker * target) { //long xdelta,ydelta; //xdelta = (long) (target->xpos - xpos) + // (long) ( (target->sizex - sizex) / 2 ); //ydelta = (long) (target->ypos - ypos) + // (long) ( (target->sizey - sizey) / 2 ); //return (long) (xdelta*xdelta + ydelta*ydelta); return ( abs(target->xpos - xpos) + abs(target->ypos - ypos) ); } long walker::distance_to_ob_center(walker * target) { long xdelta,ydelta; xdelta = (long) (target->xpos - xpos) + (long) ( (target->sizex - sizex) / 2 ); ydelta = (long) (target->ypos - ypos) + (long) ( (target->sizey - sizey) / 2 ); return (long) (xdelta*xdelta + ydelta*ydelta); } unsigned char walker::query_team_color() { // Debugging .. //if (foe && !foe->dead) return (unsigned char) (team_num*16+40); //else // return (unsigned char) (7*16 + 40); } long walker::is_friendly(walker *target) { // is_friendly determines if _target_ is "friendly" // towards this walker. short allied_mode; short has_myguy; walker *headguy; walker *headus, *headtarget; // In case we're passed a null pointer somehow, // we're always unfriendly :) if (target == NULL) return 0; // If either of us is dead, we're also unfriendly :) if (dead || target->dead) return 0; // who's the top on our chains (ie, weapon->summoned->mage) // First us .. headguy = this; while (headguy->owner && (headguy->owner->dead == 0) && (headguy->owner != headguy) ) headguy = headguy->owner; headus = headguy; // Now our target .. headguy = target; while (headguy->owner && (headguy->owner->dead == 0) && (headguy->owner != headguy) ) headguy = headguy->owner; headtarget = headguy; // First, get our allied setting from screen .. // 0 is "enemy," and non-zero is "friendly" allied_mode = myscreen->allied_mode; // Now, if we or the target don't contain a "myguy" pointer, // then we don't care about allied_mode, and we'll // treat our state as always in 'enemy' mode if (headtarget->myguy == NULL || headus->myguy == NULL) has_myguy = 0; else has_myguy = 1; // Is allied mode set to zero (enemy)? // If so, then if our team numbers don't match, // we are not friendly if (myscreen->allied_mode == 0 || has_myguy == 0) { return (headus->team_num == headtarget->team_num); } // end of allied_mode OFF // If we're in 'friendly' mode, then everyone with // a "myguy" pointer (a real, saved character) // is friendly to each other .. // By now we know that both us and the target have // myguy's, so we're friendly return 1; }