text: //-------------------------------------------------------------------------
  /*
  Copyright (C) 1997, 2005 - 3D Realms Entertainment
  
  This file is part of Shadow Warrior version 1.2
  
  Shadow Warrior 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  
  Original Source: 1997 - Frank Maddin and Jim Norwood
  Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
  */
  //-------------------------------------------------------------------------
  #include "ns.h"
  
  #include "build.h"
  
  #include "names2.h"
  #include "panel.h"
  #include "game.h"
  #include "tags.h"
  #include "player.h"
  #include "mclip.h"
  
  BEGIN_SW_NS
  
  
  Collision MultiClipMove(PLAYER* pp, int z, int floor_dist)
  {
      int i;
      vec3_t opos[MAX_CLIPBOX], pos[MAX_CLIPBOX];
      SECTOR_OBJECT* sop = pp->sop;
      short ang;
      short min_ndx = 0;
      int min_dist = 999999;
      int dist;
  
      int ret;
      Collision min_ret{};
  
      int xvect,yvect;
  
      for (i = 0; i < sop->clipbox_num; i++)
      {
          // move the box to position instead of using offset- this prevents small rounding errors
          // allowing you to move through wall
          ang = NORM_ANGLE(pp->angle.ang.asbuild() + sop->clipbox_ang[i]);
  
          vec3_t spos = { pp->pos.X, pp->pos.Y, z };
  
          xvect = sop->clipbox_vdist[i] * bcos(ang);
          yvect = sop->clipbox_vdist[i] * bsin(ang);
          Collision coll;
          clipmove(spos, &pp->cursector, xvect, yvect, (int)sop->clipbox_dist[i], Z(4), floor_dist, CLIPMASK_PLAYER, coll, 1);
  
          if (coll.type != kHitNone)
          {
              // hit something moving into start position
              min_dist = 0;
              min_ndx = i;
              // ox is where it should be
              opos[i].X = pos[i].X = pp->pos.X + MulScale(sop->clipbox_vdist[i], bcos(ang), 14);
              opos[i].Y = pos[i].Y = pp->pos.Y + MulScale(sop->clipbox_vdist[i], bsin(ang), 14);
  
              // spos.x is where it hit
              pos[i].X = spos.X;
              pos[i].Y = spos.Y;
  
              // see the dist moved
              dist = ksqrt(SQ(pos[i].X - opos[i].X) + SQ(pos[i].Y - opos[i].Y));
  
              // save it off
              if (dist < min_dist)
              {
                  min_dist = dist;
                  min_ndx = i;
                  min_ret = coll;
              }
          }
          else
          {
              // save off the start position
              opos[i] = pos[i] = spos;
              pos[i].Z = z;
  
              // move the box
              clipmove(pos[i], &pp->cursector, pp->vect.X, pp->vect.Y, (int)sop->clipbox_dist[i], Z(4), floor_dist, CLIPMASK_PLAYER, coll);
  
              // save the dist moved
              dist = ksqrt(SQ(pos[i].X - opos[i].X) + SQ(pos[i].Y - opos[i].Y));
  
              if (dist < min_dist)
              {
                  min_dist = dist;
                  min_ndx = i;
                  min_ret = coll;
              }
          }
      }
  
      // put posx and y off from offset
      pp->pos.X += pos[min_ndx].X - opos[min_ndx].X;
      pp->pos.Y += pos[min_ndx].Y - opos[min_ndx].Y;
  
      return min_ret;
  }
  
  short MultiClipTurn(PLAYER* pp, short new_ang, int z, int floor_dist)
  {
      int i;
      SECTOR_OBJECT* sop = pp->sop;
      int ret;
      int x,y;
      short ang;
      int xvect, yvect;
      auto cursect = pp->cursector;
  
      for (i = 0; i < sop->clipbox_num; i++)
      {
          ang = NORM_ANGLE(new_ang + sop->clipbox_ang[i]);
  
          vec3_t pos = { pp->pos.X, pp->pos.Y, z };
  
          xvect = sop->clipbox_vdist[i] * bcos(ang);
          yvect = sop->clipbox_vdist[i] * bsin(ang);
  
          // move the box
          Collision coll;
          clipmove(pos, &cursect, xvect, yvect, (int)sop->clipbox_dist[i], Z(4), floor_dist, CLIPMASK_PLAYER, coll);
  
          ASSERT(cursect);
  
          if (coll.type != kHitNone)
          {
              // attempt to move a bit when turning against a wall
              //ang = NORM_ANGLE(ang + 1024);
              //pp->xvect += 20 * bcos(ang);
              //pp->yvect += 20 * bsin(ang);
              return false;
          }
      }
  
      return true;
  }
  
  int testquadinsect(int *point_num, vec2_t const * q, sectortype* sect)
  {
      int i,next_i;
  
      *point_num = -1;
  
      for (i=0; i < 4; i++)
      {
          if (!inside(q[i].X, q[i].Y, sect))
          {
              *point_num = i;
  
              return false;
          }
      }
  
      for (i=0; i<4; i++)
      {
          next_i = (i+1) & 3;
          if (!cansee(q[i].X, q[i].Y,0x3fffffff, sect,
                      q[next_i].X, q[next_i].Y,0x3fffffff, sect))
          {
              return false;
          }
      }
  
      return true;
  }
  
  
  //Ken gives the tank clippin' a try...
  int RectClipMove(PLAYER* pp, int *qx, int *qy)
  {
      int i;
      vec2_t xy[4];
      int point_num;
  
      for (i = 0; i < 4; i++)
      {
          xy[i].X = qx[i] + (pp->vect.X>>14);
          xy[i].Y = qy[i] + (pp->vect.Y>>14);
      }
  
      //Given the 4 points: x[4], y[4]
      if (testquadinsect(&point_num, xy, pp->cursector))
      {
          pp->pos.X += (pp->vect.X>>14);
          pp->pos.Y += (pp->vect.Y>>14);
          return true;
      }
  
      if (point_num < 0)
          return false;
  
      if ((point_num == 0) || (point_num == 3))   //Left side bad - strafe right
      {
          for (i = 0; i < 4; i++)
          {
              xy[i].X = qx[i] - (pp->vect.Y>>15);
              xy[i].Y = qy[i] + (pp->vect.X>>15);
          }
          if (testquadinsect(&point_num, xy, pp->cursector))
          {
              pp->pos.X -= (pp->vect.Y>>15);
              pp->pos.Y += (pp->vect.X>>15);
          }
  
          return false;
      }
  
      if ((point_num == 1) || (point_num == 2))   //Right side bad - strafe left
      {
          for (i = 0; i < 4; i++)
          {
              xy[i].X = qx[i] + (pp->vect.Y>>15);
              xy[i].Y = qy[i] - (pp->vect.X>>15);
          }
          if (testquadinsect(&point_num, xy, pp->cursector))
          {
              pp->pos.X += (pp->vect.Y>>15);
              pp->pos.Y -= (pp->vect.X>>15);
          }
  
          return false;
      }
  
      return false;
  }
  
  int testpointinquad(int x, int y, int *qx, int *qy)
  {
      int i, cnt, x1, y1, x2, y2;
  
      cnt = 0;
      for (i=0; i<4; i++)
      {
          y1 = qy[i]-y;
          y2 = qy[(i+1)&3]-y;
          if ((y1^y2) >= 0) continue;
  
          x1 = qx[i]-x;
          x2 = qx[(i+1)&3]-x;
          if ((x1^x2) >= 0)
              cnt ^= x1;
          else
              cnt ^= (x1*y2-x2*y1)^y2;
      }
      return cnt>>31;
  }
  
  short RectClipTurn(PLAYER* pp, short new_ang, int *qx, int *qy, int *ox, int *oy)
  {
      int i;
      vec2_t xy[4];
      SECTOR_OBJECT* sop = pp->sop;
      short rot_ang;
      int point_num;
  
      rot_ang = NORM_ANGLE(new_ang + sop->spin_ang - sop->ang_orig);
      for (i = 0; i < 4; i++)
      {
          vec2_t const p = { ox[i], oy[i] };
          rotatepoint(pp->pos.vec2, p, rot_ang, &xy[i]);
          // cannot use sop->xmid and ymid because the SO is off the map at this point
          //rotatepoint(&sop->xmid, p, rot_ang, &xy[i]);
      }
  
      //Given the 4 points: x[4], y[4]
      if (testquadinsect(&point_num, xy, pp->cursector))
      {
          // move to new pos
          for (i = 0; i < 4; i++)
          {
              qx[i] = xy[i].X;
              qy[i] = xy[i].Y;
          }
          return true;
      }
  
      if (point_num < 0)
          return false;
  
      return false;
  }
  END_SW_NS
  