/* * XEvil(TM) Copyright (C) 1994,2000 Steve Hardt and Michael Judge * http://www.xevil.com * satan@xevil.com * * 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, the file "gpl.txt"; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA, or visit http://www.gnu.org. */ // "coord.cpp" #if X11 #ifndef NO_PRAGMAS #pragma implementation "coord.h" #endif #endif // Include Files #include "stdafx.h" #include "utils.h" #include "coord.h" #include "area.h" #ifndef NULL #define NULL 0 #endif // Functions. void Stats::add_death(time_t birthTime) { if (enabled) { time_t lifespan = time(NULL) - birthTime; if (deaths > 0) aveLifespan = (float)((aveLifespan * deaths + lifespan) / (deaths + 1)); else aveLifespan = (float)lifespan; deaths++; } } Boolean Stats::enabled = False; void Pos::read(InStreamP in) { x = in->read_signed_short(); y = in->read_signed_short(); } int Pos::get_write_length() { return 2 * sizeof(short); } void Pos::write(OutStreamP out) const { out->write_signed_short(x); out->write_signed_short(y); } int Pos::distance(const Pos &p) const { Size diff = *this - p; assert (diff.abs_2() >= 0); return (int)sqrt((double)(diff.width * diff.width + diff.height * diff.height)); //stupid MS compiler gets ambiguous call without the recast to int } int Pos::distance_2(const Pos &p) const { Size diff = *this - p; return diff.width * diff.width + diff.height * diff.height; } void Vel::read(InStreamP in) { dx = in->read_float(); dy = in->read_float(); } int Vel::get_write_length() { return 2 * sizeof(float); } void Vel::write(OutStreamP out) const { out->write_float(dx); out->write_float(dy); } Dir Size::get_dir() { if (!width && !height) { return CO_air; } if (height < 0.5 * width) { if (height > -2 * width) { if (height < -0.5 * width) { return CO_UP_R; } else { return CO_R; } } else { if (height < 2 * width) { return CO_UP; } else { return CO_UP_L; } } } else { if (height < -2 * width) { if (height < -0.5 * width) { return CO_L; } else { return CO_DN_L; } } else { if (height < 2 * width) { return CO_DN_R; } else { return CO_DN; } } } } void Size::get_dirs_4(Dir &d1,Dir &d2) { if (!width && !height) { d1 = d2 = CO_air; return; } if (width) { d1 = (width > 0) ? CO_R : CO_L; } if (height) { d2 = (height > 0) ? CO_DN : CO_UP; } else { d2 = d1; } if (!width) { d1 = d2; } } void Size::read(InStreamP in) { width = in->read_char(); height = in->read_char(); } int Size::get_write_length() { return 2 * sizeof(char); } void Size::write(OutStreamP out) const { assert(width <= UCHAR_MAX && height <= UCHAR_MAX); out->write_char((u_char)width); out->write_char((u_char)height); } void Size::read_32(InStreamP in) { width = in->read_int(); height = in->read_int(); } int Size::get_write_length_32() { return 2 * sizeof(int); } void Size::write_32(OutStreamP out) const { out->write_int(width); out->write_int(height); } float Size::cross(const Vel &vel) { return width * vel.dy - height * vel.dx; } float Size::dot(const Vel &vel) { return width * vel.dx + height * vel.dy; } void Loc::read(InStreamP in) { r = in->read_short(); c = in->read_short(); } int Loc::get_write_length() { return 2 * sizeof(short); } void Loc::write(OutStreamP out) const { assert(r <= USHRT_MAX && c <= USHRT_MAX); out->write_short((u_short)r); out->write_short((u_short)c); } Boolean Box::overlap(const Loc &l) { if ((l.c >= loc.c) && (l.c < loc.c + dim.colMax) && (l.r >= loc.r) && (l.r < loc.r + dim.rowMax)) { return True; } return False; } Vel Vel::shrink(float k) const { Vel ret = *this; if (ret.dx > k) { ret.dx -= k; } else if (ret.dx < -k) { ret.dx += k; } else { ret.dx = 0.0f; } if (ret.dy > k) { ret.dy -= k; } else if (ret.dy < -k) { ret.dy += k; } else { ret.dy = 0.0f; } return ret; } void Vel::damp(float k) { if (dx > k) { dx -= k; } else if (dx < -k) { dx += k; } else { dx = 0.0f; } if (dy > k) { dy -= k; } else if (dy < -k) { dy += k; } else { dy = 0.0f; } } Boolean Vel::is_zero() const { Boolean ret; if ((dx == 0) && (dy == 0)) { ret = True; } else { ret = False; } return ret; } Dir Vel::get_dir() const { if (!dx && !dy) { return CO_air; } if (dy < 0.5 * dx) { if (dy > -2 * dx) { if (dy < -0.5 * dx) { return CO_UP_R; } else { return CO_R; } } else { if (dy < 2 * dx) { return CO_UP; } else { return CO_UP_L; } } } else { if (dy < -2 * dx) { if (dy < -0.5 * dx) { return CO_L; } else { return CO_DN_L; } } else { if (dy < 2 * dx) { return CO_DN_R; } else { return CO_DN; } } } } void Vel::limit(float k) { assert (k >= 0); if (dx > k) { dx = k; } if (dx < -k) { dx = -k; } if (dy > k) { dy = k; } if (dy < -k) { dy = -k; } } void Vel::get_dirs_4(Dir in[4],Dir out[4],int &inNum,int &outNum) { inNum = 0; outNum = 0; if (dx > 0) { in[inNum] = CO_R; inNum++; } else { out[outNum] = CO_R; outNum++; } if (dy > 0) { in[inNum] = CO_DN; inNum++; } else { out[outNum] = CO_DN; outNum++; } if (dx < 0) { in[inNum] = CO_L; inNum++; } else { out[outNum] = CO_L; outNum++; } if (dy < 0) { in[inNum] = CO_UP; inNum++; } else { out[outNum] = CO_UP; outNum++; } assert(inNum + outNum == 4); } void Dim::read(InStreamP in) { rowMax = in->read_short(); colMax = in->read_short(); } int Dim::get_write_length() { return 2 * sizeof(short); } void Dim::write(OutStreamP out) const { assert(rowMax <= USHRT_MAX && colMax <= USHRT_MAX); out->write_short((u_short)rowMax); out->write_short((u_short)colMax); } Boolean operator == (const Loc &l1, const Loc &l2) { return l1.r == l2.r && l1.c == l2.c; } Boolean operator == (const Pos &p1, const Pos &p2) { return p1.x == p2.x && p1.y == p2.y; } Boolean operator == (const Vel &v1, const Vel &v2) { return v1.dx == v2.dx && v1.dy == v2.dy; } Boolean operator == (const Size &s1, const Size &s2) { if ((s1.width == s2.width) && (s1.height == s2.height)) return True; else return False; } Boolean operator == (const GLoc &g1,const GLoc &g2) { return (g1.horiz == g2.horiz) && (g1.vert == g2.vert); } Boolean operator == (const Dim &d1,const Dim &d2) { return d1.rowMax == d2.rowMax && d1.colMax == d2.colMax; } Boolean operator == (const Rooms &r1,const Rooms &r2) { return r1.acrossMax == r2.acrossMax && r1.downMax == r2.downMax; } Boolean operator != (const GLoc &g1,const GLoc &g2) { return (g1.horiz != g2.horiz) || (g1.vert != g2.vert); } Pos operator + (const Pos &pos,const Size &size) { Pos ret; ret.x = pos.x + size.width; ret.y = pos.y + size.height; return ret; } Pos operator - (const Pos &pos,const Size &size) { Pos ret; ret.x = pos.x - size.width; ret.y = pos.y - size.height; return ret; } Size operator - (const Pos &p1,const Pos &p2) { Size ret; ret.width = p1.x - p2.x; ret.height = p1.y - p2.y; return ret; } Pos operator + (const Pos &pos, const Vel &vel) { Pos ret; /* We want it to round towards zero so that it does the same thing for something going to the right as to the left. */ ret.x = pos.x + (int)vel.dx; ret.y = pos.y + (int)vel.dy; /* ret.x = pos.x + (int)trunc(vel.dx); ret.y = pos.y + (int)trunc(vel.dy); */ return ret; } Size operator * (float k,const Size &size) { Size ret; ret.width = (int)floor(k * size.width); ret.height = (int)floor(k * size.height); return ret; } Vel operator + (const Vel &v1,const Vel &v2) { Vel ret(v1.dx + v2.dx,v1.dy + v2.dy); return ret; } Vel operator + (const Vel &vel, const Acc &acc) { Vel ret(vel.dx + acc.ddx,vel.dy + acc.ddy); return ret; } Vel operator + (const Vel &vel, const Size &size) { Vel ret(vel.dx + size.width,vel.dy + size.height); return ret; } Vel operator * (float k,const Vel &vel) { Vel ret(k * vel.dx, k * vel.dy); return ret; } Vel operator / (float k,const Vel &vel) { Vel ret(k / vel.dx, k / vel.dy); return ret; } Vel operator + (float k,const Vel &vel) { Vel ret(k + vel.dx,k + vel.dy); return ret; } Acc operator * (int k,const Acc &acc) { Acc ret; ret.ddx = k * acc.ddx; ret.ddy = k * acc.ddy; return ret; } Size operator + (const Size& s1,const Size& s2) { Size ret; ret.set(s1.width + s2.width,s1.height + s2.height); return ret; } #if 0 Size& operator += (Size& s1,const Size& s2) { s1.width += s2.width; s1.height += s2.height; return s1; } #endif Boolean Coord::is_dir_pure(Dir dir) { return dir >= CO_R && dir < (CO_R + CO_DIR_PURE); } Dir Coord::dir_opposite(Dir dir) { if (dir == CO_air) return CO_air; assert(dir >= CO_R && dir < CO_DIR_MAX); return ((dir - CO_R + CO_DIR_PURE / 2) % CO_DIR_PURE) + CO_R; } Dir Coord::movement_dir_4(Dir dir) { switch(dir) { case CO_center: case CO_air: case CO_climb: case CO_r: case CO_dn: case CO_l: case CO_up: return CO_air; case CO_center_R: case CO_dn_R: case CO_up_R: case CO_air_R: case CO_climb_R: case CO_UP_R: case CO_UP_R_R: case CO_R: case CO_DN_R_R: case CO_DN_R: return CO_R; case CO_air_DN: case CO_r_DN: case CO_l_DN: case CO_climb_DN: case CO_DN_DN_R: case CO_DN: case CO_DN_DN_L: return CO_DN; case CO_center_L: case CO_dn_L: case CO_up_L: case CO_air_L: case CO_climb_L: case CO_UP_L: case CO_UP_L_L: case CO_L: case CO_DN_L_L: case CO_DN_L: return CO_L; case CO_air_UP: case CO_r_UP: case CO_l_UP: case CO_climb_UP: case CO_UP_UP_R: case CO_UP: case CO_UP_UP_L: return CO_UP; default: assert(0); return CO_air; } } Dir Coord::parallel_dir(Stance stance) { switch (stance) { case CO_r: case CO_l: case CO_climb: return CO_DN; case CO_up: case CO_dn: case CO_center: return CO_R; case CO_air: return CO_air; default: assert(0); return CO_air; } } Pos Coord::shot_initial_pos(const Area &area,Touching touching, const Size &shotSize,Dir shotDir) { // Return value; Pos ret; Pos pos; Size size; area.get_rect(pos,size); // Set x coord. switch (shotDir) { case CO_UP_R: case CO_UP_R_R: case CO_R: case CO_DN_R_R: case CO_DN_R: if (shotSize.width <= size.width) ret.x = pos.x + size.width - shotSize.width; else ret.x = pos.x; break; case CO_UP_L: case CO_UP_L_L: case CO_L: case CO_DN_L_L: case CO_DN_L: if (shotSize.width <= size.width) ret.x = pos.x; else ret.x = pos.x + size.width - shotSize.width; break; case CO_UP_UP_L: case CO_UP: case CO_UP_UP_R: case CO_DN_DN_L: case CO_DN: case CO_DN_DN_R: case CO_air: ret.x = (int)(pos.x + size.width * .5 - shotSize.width * .5); if (touching == CO_l) { ret.x = Utils::maximum(ret.x,pos.x); } else if (touching == CO_r) { ret.x = Utils::minimum(ret.x + shotSize.width,pos.x + size.width) - shotSize.width; } break; default: assert(0); } // Set y coord. switch (shotDir) { case CO_DN_L: case CO_DN_DN_L: case CO_DN: case CO_DN_DN_R: case CO_DN_R: if (shotSize.height <= size.height) ret.y = pos.y + size.height - shotSize.height; else ret.y = pos.y; break; case CO_UP_L: case CO_UP_UP_L: case CO_UP: case CO_UP_UP_R: case CO_UP_R: if (shotSize.height <= size.height) ret.y = pos.y; else ret.y = pos.y + size.height - shotSize.height; break; case CO_UP_R_R: case CO_R: case CO_DN_R_R: case CO_UP_L_L: case CO_L: case CO_DN_L_L: case CO_air: ret.y = (int)(pos.y + size.height * .5 - shotSize.height * .5); if (touching == CO_up) { ret.y = Utils::maximum(ret.y,pos.y); } else if (touching == CO_dn) { ret.y = Utils::minimum(ret.y + shotSize.height,pos.y + size.height) - shotSize.height; } break; default: assert(0); } return ret; } NetData::NetData() { lastModified = 0; fClocked = False; sent = False; // means is unspecified. tickType = TICK_MAX; } Touching Coord::dir_to_touching(Dir dir) { switch (dir) { case CO_r: case CO_r_DN: case CO_r_UP: return CO_r; case CO_dn: case CO_dn_R: case CO_dn_L: case CO_center: case CO_center_R: case CO_center_L: return CO_dn; case CO_l: case CO_l_DN: case CO_l_UP: return CO_l; case CO_up: case CO_up_R: case CO_up_L: return CO_up; default: return CO_air; } } Touching Coord::zero_offset_generator(Dir) { // See Coord::generate_offsets. return CO_UP_L; } void Coord::generate_offsets(Size offsets[CO_DIR_MAX], const Size sizes[CO_DIR_MAX],const Size &sizeMax, Touching (*dirToTouching)(Dir)) { Dir dir; for (dir = 0; dir < CO_DIR_MAX; dir++) { Touching touching = dirToTouching(dir); assert(touching == CO_r || touching == CO_dn || touching == CO_l || touching == CO_up || touching == CO_air || touching == CO_UP_L); // CO_UP_L is a flag that means the offsets should both be zero. if (touching == CO_UP_L) { offsets[dir].width = 0; offsets[dir].height = 0; } // Normal touching values. else { if (touching == CO_r) { offsets[dir].width = sizeMax.width - sizes[dir].width; } else if (touching == CO_l) { offsets[dir].width = 0; } else { offsets[dir].width = (sizeMax.width - sizes[dir].width) >> 1; } if (touching == CO_dn) { offsets[dir].height = sizeMax.height - sizes[dir].height; } else if (touching == CO_up) { offsets[dir].height = 0; } else { offsets[dir].height = (sizeMax.height - sizes[dir].height) >> 1; } } // not CO_UP_L } } signed char NetData::gClocked = -1; // disabled by default. unsigned int OneTransform::compute_key(const TransformType* transforms,int tNum) { assert(TRANSFORMS_MAX == 2); // Or we need to change this code. assert(tNum <= TRANSFORMS_MAX); if (tNum == 0 || (tNum == 1 && transforms[0] == TR_NONE)) { return 0x0; } if (tNum == 1) { switch (transforms[0]) { case TR_RT_1: return 0x1; case TR_RT_2: return 0x2; case TR_RT_3: return 0x3; case TR_RF_X: return 0x4; } } if (tNum == 2) { // Reflection always goes before rotation in canonical order. assert(transforms[0] == TR_RF_X); switch (transforms[1]) { case TR_RT_1: return 0x5; case TR_RT_2: return 0x6; case TR_RT_3: return 0x7; } } // Shouldn't get here. assert(0); return 0x0; } Boolean Transform2D::is_base(Dir d,TransformMap* override) { // Check whether to use override or default. OneTransform* trans; if (override && (*override)[d].transformsNum != TR_USE_DEFAULT) { trans = &(*override)[d]; } else { trans = &transforms[d]; } return trans->transformsNum == 0; } Dir Transform2D::get_base(Dir d,TransformMap* override) { assert(!is_base(d,override)); // Check whether to use override or default. OneTransform* trans; if (override && (*override)[d].transformsNum != TR_USE_DEFAULT) { trans = &(*override)[d]; } else { trans = &transforms[d]; } return trans->base; } const TransformType* Transform2D::get_transforms(int &tNum,Dir d, TransformMap* override) { assert(!is_base(d,override)); // Check whether to use override or default. OneTransform* trans; if (override && (*override)[d].transformsNum != TR_USE_DEFAULT) { trans = &(*override)[d]; } else { trans = &transforms[d]; } // Special check for the identity transformation. if (trans->transforms[0] == TR_NONE) { assert(trans->transformsNum == 1); tNum = 0; return NULL; } tNum = trans->transformsNum; return trans->transforms; } Pos Transform2D::apply(TransformType tt,const Pos &pos,const Size &size) { switch (tt) { case TR_NONE: return pos; case TR_RT_1: return Pos(size.height - 1 - pos.y,pos.x); case TR_RT_2: return Pos(size.width - 1 - pos.x,size.height - 1 - pos.y); case TR_RT_3: return Pos(pos.y,size.width - 1 - pos.x); case TR_RF_X: return Pos(size.width - 1 - pos.x,pos.y); default: assert(0); // Will all compilers accept this syntax? return Pos(); } } Size Transform2D::apply(TransformType tt,const Size &size) { switch (tt) { case TR_NONE: return size; case TR_RT_1: case TR_RT_3: { Size ret; ret.set(size.height,size.width); return ret; } case TR_RT_2: case TR_RF_X: return size; default: assert(0); // Will all compilers accept this syntax? return size; } } Pos Transform2D::apply_all(Dir d,const Pos &pos,const Size &size, TransformMap* override) { int tNum; const TransformType* transforms = Transform2D::get_transforms(tNum,d,override); Pos ret = pos; for (int m = 0; m < tNum; m++) { ret = Transform2D::apply(transforms[m],ret,size); } return ret; } // Transform2D::transforms is defined in transform.bitmaps included in // physical.cpp