Esempio n. 1
0
void SizeTest::misc() {
    Ilwis::Size<double> sz(3.45, 2.67, 8.0);

    DOTEST( APPROX(sz.linearSize(), 48,0.001), "volume test");

    Ilwis::Size<> sz2(200,400, 300);
    DOTEST(sz2.contains(24,23,67), "valid contains test");
    DOTEST(sz2.contains(700,23,67) == false, "valid contains test that fails");
}
Esempio n. 2
0
void
quaternion_togl(Quaternion *quat)
{
	if (APPROX(fabs(quat->w), 1)) { return; }

	if (quat->w > 1) { quaternion_normalize(quat); }

	/* get the angle, but turn us around 180 degrees */
	/* printf ("togl: setting rotation %f %f %f %f\n",quat->w,quat->x,quat->y,quat->z);*/
	FW_GL_ROTATE_RADIANS(2.0 * acos(quat->w), quat->x, quat->y, quat->z);
}
Esempio n. 3
0
void
quaternion_normalize(Quaternion *quat)
{
	double n = quaternion_norm(quat);
	if (APPROX(n, 1)) {
		return;
	}
	quat->w /= n;
	quat->x /= n;
	quat->y /= n;
	quat->z /= n;
}
Esempio n. 4
0
/*
 * Quaternion (q = (w, v)) to VRML rotation (axis, angle):
 *
 * angle = 2 * acos(q.w)
 * axis.x = q.x / scale
 * axis.y = q.y / scale
 * axis.z = q.z / scale
 *
 * unless scale = 0, in which case, we'll use the default VRML
 * rotation
 *
 * One can use either scale = sqrt(q.x^2 + q.y^2 + q.z^2) or
 * scale = sin(acos(q.w)).
 * Since we are using unit quaternions, 1 = w^2 + x^2 + y^2 + z^2.
 * Also, acos(x) = asin(sqrt(1 - x^2)) (for x >= 0, but since we don't
 * actually compute asin(sqrt(1 - x^2)) let's not worry about it).
 * scale = sin(acos(q.w)) = sin(asin(sqrt(1 - q.w^2)))
 *       = sqrt(1 - q.w^2) = sqrt(q.x^2 + q.y^2 + q.z^2)
 */
void
quaternion_to_vrmlrot(const Quaternion *quat, double *x, double *y, double *z, double *a)
{
	double scale = sqrt(VECSQ(*quat));
	if (APPROX(scale, 0)) {
		*x = 0;
		*y = 0;
		*z = 1;
		*a = 0;
	} else {
		*x = quat->x / scale;
		*y = quat->y / scale;
		*z = quat->z / scale;
		*a = 2 * acos(quat->w);
	}
}
Esempio n. 5
0
/* getLineCollision

   Given two lines, one stationary and one moving, check if they collided.
   This works by looking at the path travelled by each endpoint of the moving
   line and seeing if that path intersects with the stationary line.  If that
   doesn't detect a collision, do the same test for the endpoints of the
   stationary line.  I think this should detect all possible collisions between
   two lines of any length if one of them is moving.  The values returned
   indicate which point on which line was found to be colliding.

*/
int
getLineCollision(Line mov_line, Line stat_line, Velocity v, Time dt)
{

  Line path;
  
  /* Exit now if the dot product of the two lines' normals is >= 0 */
  if (dot(normal(mov_line), normal(stat_line)) >= 0) return -1;

  /* Create a line representing the path that p1 on the moving line
     travelled: */
  path.p2 = mov_line.p1;
  path.p1.x = path.p2.x - APPROX(v.x * dt);
  path.p1.y = path.p2.y - APPROX(v.y * dt);
  
  if (intersect(path, stat_line)) return L1P1;
  
  /* No collision for p1, try p2: */
  path.p2 = mov_line.p2;
  path.p1.x = path.p2.x - APPROX(v.x * dt);
  path.p1.y = path.p2.y - APPROX(v.y * dt);
  
  if (intersect(path, stat_line)) return L1P2;
  
  /* Pretend the moving line was stationary and the stationary line moved,
     and test for intersection with the stationary line's paths: */
  path.p1 = stat_line.p1;
  path.p2.x = path.p1.x + APPROX(v.x * dt);
  path.p2.y = path.p1.y + APPROX(v.y * dt);
  
  if (intersect(path, mov_line)) return L2P1;
  
  /* Try p2: */
  path.p1 = stat_line.p2;
  path.p2.x = path.p1.x + APPROX(v.x * dt);
  path.p2.y = path.p1.y + APPROX(v.y * dt);
  
  if (intersect(path, mov_line)) return L2P2;
  
  /* If we still haven't found a collision, return -1 for failure: */

  return -1;

}
Esempio n. 6
0
void
vrmlrot_to_quaternion(Quaternion *quat, const double x, const double y, const double z, const double a)
{
	double s;
	double scale = sqrt((x * x) + (y * y) + (z * z));

	/* no rotation - use (multiplication ???) identity quaternion */
	if (APPROX(scale, 0)) {
		quat->w = 1;
		quat->x = 0;
		quat->y = 0;
		quat->z = 0;

	} else {
		s = sin(a/2);
		/* normalize rotation axis to convert VRML rotation to quaternion */
		quat->w = cos(a / 2);
		quat->x = s * (x / scale);
		quat->y = s * (y / scale);
		quat->z = s * (z / scale);
		quaternion_normalize(quat);
	}
}
Esempio n. 7
0
/* col_collisionResponse
   Given an object which is colliding and rough info about the collision,
   change the position and velocity of the object accordingly.  If the object
   is colliding with another object, modify the other object's velocity and
   position as well.

   Also, send impulse signals to objects whose velocities changed.
*/
void
col_collisionResponse(Object *obj, Collision *coll_info, Time dt)
{

  Signal sig_a, sig_b;
  Vector impulse_a, impulse_b;
  Velocity v_a, v_b, rel_v_a;
  Point pos_a, new_pos_a, pos_b, new_pos_b;
  int mass_a, mass_b;

  float k_a, k_b, k;    /* Coefficients of elasticity */

  /* Get object a's position, velocity, mass, and elasticity: */
  v_a = obj_getObjVel(obj);
  new_pos_a = pos_a = obj_getObjPos(obj);
  mass_a = obj_getObjMass(obj);
  k_a = obj_getObjElasticity(obj);

  /* If b is an object, get its position, velocity, mass and elasticity*/
  if (coll_info->type == OBJ_TYPE)
    {
      v_b = obj_getObjVel(coll_info->other.obj);
      new_pos_b = pos_b = obj_getObjPos(coll_info->other.obj);
      mass_b = obj_getObjMass(coll_info->other.obj);
      k_b = obj_getObjElasticity(coll_info->other.obj);
    }
  else
    {
      v_b.x = 0;
      v_b.y = 0;
      k_b = 1;
    }

  /* The elasticity for the collision is the product of each object's
     elasticity */
  k = k_a * k_b;

  /* Do these detections using a's relative velocity to b */
  rel_v_a.x = v_a.x - v_b.x;
  rel_v_a.y = v_a.y - v_b.y;


  /* If a's boundary is a rectangle: */
  if (coll_info->a.type == RECT)
    {

      /* If both boundaries are rectangles, all we know about the collision 
	 is that they are overlapping, so we'll start from there: */

      if (coll_info->b.type == RECT)
	{

	  Vector n = {0, 0};
	  int x_depth = 0, y_depth = 0;

	  /* Find the overlap depths of the rectangles.  If there is an overlap
	     in one dimension and the overlap is less than the distance 
	     travelled in that dimension, there might have been a collision
	     in that dimension.  If so, set one component of a normal vector
	     of the colliding surface to remember. */
	  if (rel_v_a.x > 0)
	    {
	      x_depth = coll_info->a.b.rect.p2.x - coll_info->b.b.rect.p1.x;
	      if (APPROX(rel_v_a.x * dt) >= x_depth) n.x = -1;
	    }
	  else if (rel_v_a.x < 0)
	    {
	      x_depth = coll_info->a.b.rect.p1.x - coll_info->b.b.rect.p2.x;
	      if (APPROX(rel_v_a.x * dt) <= x_depth) n.x = 1;
	    }

	  if (rel_v_a.y > 0)
	    {
	      y_depth = coll_info->a.b.rect.p2.y - coll_info->b.b.rect.p1.y;
	      if (APPROX(rel_v_a.y * dt) >= y_depth) n.y = -1;
	    }
	  else if (rel_v_a.y < 0)
	    {
	      y_depth = coll_info->a.b.rect.p1.y - coll_info->b.b.rect.p2.y;
	      if (APPROX(rel_v_a.y * dt) <= y_depth) n.y = 1;
	    }


	  /* If we have both an x and y overlap, it is not immediately obvious
	     whether the collision was on a side or a top or bottom.  So what
	     we do is, move backwards along the velocity vector and see if
	     there is still y overlap at that point.  If there is, we know
	     it was an x-collision. */
	  if (n.x != 0 && n.y != 0)
	    {

	      /* The y-distance travelled at the point where there is no
		 x-overlap: */
	      int dy;
	      dy = APPROX((APPROX(rel_v_a.x * dt) - x_depth) * APPROX(rel_v_a.y * dt) / APPROX(rel_v_a.x * dt));

	      if (n.y == -1)
		{
		  if (y_depth > dy) n.y = 0;
		  else n.x = 0;
		}
	      else 
		{
		  if (y_depth < dy) n.y = 0;
		  else n.x = 0;
		}

	    }


	  /* Right, now we know which side the collision occurred on, and
	     we know how deeply the rectangles overlap, so we can set the
	     position and velocity of the object accordingly: */


	  /* If both things are objects: */
	  if (coll_info->type == OBJ_TYPE)
	    {
	      float a_a, a_b, optimizedP;
	      
	      /* We are using n as the normal for the surface of impact between
		 the two objects */
	      
	      a_a = dot(v_a, n);
	      a_b = dot(v_b, n);
	      
	      /* Perfectly inelastic when k = 0, perfectly elastic when k = 1.
		 Really bouncy when k > 1. */
	      optimizedP = ((1 + k) * (a_a - a_b)) / (mass_a + mass_b);
	      
	      
	      impulse_a.x = -optimizedP * mass_b * n.x;
	      impulse_a.y = -optimizedP * mass_b * n.y;
	      
	      impulse_b.x = optimizedP * mass_a * n.x;
	      impulse_b.y = optimizedP * mass_a * n.y;
	      
	      /* Move both of the objects out of collision: */

	      new_pos_a.x = pos_a.x + rint(x_depth / 2) * n.x + n.x;
	      new_pos_a.y = pos_a.y + rint(y_depth / 2) * n.y + n.y;
	      new_pos_b.x = pos_b.x - rint(x_depth / 2) * n.x - n.x;
	      new_pos_b.y = pos_b.y - rint(y_depth / 2) * n.y - n.y;
	      
	      	      
	    }
	  /* Otherwise, just change the position and velocity of the one
	     object. */
	  else
	    {
	      impulse_a.x = -n.x * n.x * v_a.x * (1 + k);
	      impulse_a.y = -n.y * n.y * v_a.y * (1 + k);
	      new_pos_a.x = pos_a.x + x_depth * n.x + n.x;
	      new_pos_a.y = pos_a.y + y_depth * n.y + n.y;
	    }

	}
    }
  /* If a's boundary is a line: */
  else if (coll_info->a.type == LINE)
    {

      /* If b's boundary is also a line: */
      if (coll_info->b.type == LINE)
	{

	  /* Given that we know which point on which line intersected with the
	     other line, we can now find the exact point where that happened:
	  */
	  Point p;
	  Line l1, l2;

	  int x_depth = 0, y_depth = 0;
	  Vector n;

	  switch (coll_info->point_collided)
	    {
	    case L1P1:
	      l1.p2 = coll_info->a.b.line.p1;
	      l1.p1.x = l1.p2.x - APPROX(rel_v_a.x * dt);
	      l1.p1.y = l1.p2.y - APPROX(rel_v_a.y * dt);

	      l2 = coll_info->b.b.line;

	      p = intersection(l1, l2);
	      x_depth = l1.p2.x - p.x;
	      y_depth = l1.p2.y - p.y;
	      

	      break;
	    case L1P2:
	      l1.p2 = coll_info->a.b.line.p2;
	      l1.p1.x = l1.p2.x - APPROX(rel_v_a.x * dt);
	      l1.p1.y = l1.p2.y - APPROX(rel_v_a.y * dt);

	      l2 = coll_info->b.b.line;

	      
	      p = intersection(l1, l2);
	      x_depth = l1.p2.x - p.x;
	      y_depth = l1.p2.y - p.y;

	      break;
	    case L2P1:
	      l1.p1 = coll_info->b.b.line.p1;
	      l1.p2.x = l1.p1.x + APPROX(rel_v_a.x * dt);
	      l1.p2.y = l1.p1.y + APPROX(rel_v_a.y * dt);

	      l2 = coll_info->a.b.line;

	      p = intersection(l1, l2);
	      x_depth = p.x - l1.p1.x;
	      y_depth = p.y - l1.p1.y;

	      break;
	    case L2P2:
	      l1.p1 = coll_info->b.b.line.p2;
	      l1.p2.x = l1.p1.x + APPROX(rel_v_a.x * dt);
	      l1.p2.y = l1.p1.y + APPROX(rel_v_a.y * dt);

	      l2 = coll_info->a.b.line;

	      p = intersection(l1, l2);
	      x_depth = p.x - l1.p1.x;
	      x_depth = p.y - l1.p1.y;

	      break;
	    default:
	      printf("Error:  You suck\n");
	      break;
	    }

	  /* Get the normal of the b's line boundary: */
	  n = normal(coll_info->b.b.line);

	  /* If the collision is between two objects, 
	     do some fancy pants
	     momentum physics, which I found on
	     http://www.gamasutra.com/features/20020118/vandenhuevel_03.htm */
	  if (coll_info->type == OBJ_TYPE)
	    {
	      float a_a, a_b, optimizedP;

	      /* We are using n as the normal for the surface of impact between
		 the two objects */

	      a_a = dot(v_a, n);
	      a_b = dot(v_b, n);

	      /* Perfectly inelastic when k = 0, perfectly elastic when k = 1.
		 Really bouncy when k > 1. */
	      optimizedP = ((1 + k) * (a_a - a_b)) / (mass_a + mass_b);


	      impulse_a.x = -optimizedP * mass_b * n.x;
	      impulse_a.y = -optimizedP * mass_b * n.y;

	      impulse_b.x = optimizedP * mass_a * n.x;
	      impulse_b.y = optimizedP * mass_a * n.y;


	      /* Move both of the objects out of collision: */
	      new_pos_a.x = pos_a.x - rint(x_depth / 2) + rint(n.x);
	      new_pos_a.y = pos_a.y - rint(y_depth / 2) + rint(n.y);
	      new_pos_b.x = pos_b.x + rint(x_depth / 2) - rint(n.x);
	      new_pos_b.y = pos_b.y + rint(y_depth / 2) - rint(n.y);
	      
	    }

	  /* The tile is not movable, so just do some simple
	     physics: */
	  else
	    {
	      float v_dot_n;

	      /* Move a back to the point of intersection, plus a little
		 bit more in the direction of the line's normal: */
	      
	      new_pos_a.x = pos_a.x - x_depth + rint(n.x);
	      new_pos_a.y = pos_a.y - y_depth + rint(n.y);


	      v_dot_n = dot(v_a, n);
	      
	      v_dot_n *= 1 + k;
	      
	      impulse_a.x = -v_dot_n * n.x;
	      impulse_a.y = -v_dot_n * n.y;
	    }



	}

    }
  /* If a's boundary is a circle: */
  else if (coll_info->a.type == CIRCLE)
    {
      printf("Sorry, no circle collisions yet.\n");
    }


  /* Update the position and velocity: */
  v_a.x += impulse_a.x;
  v_a.y += impulse_a.y;
  obj_setObjVel(obj, v_a);
  obj_setObjPos(obj, new_pos_a);

  /* Send an impulse signal */
  sig_a.type = IMPULSE_SIG;
  sig_a.sig.imp.vec = impulse_a;
  sig_a.sig.imp.hit.type = coll_info->type;
  if (coll_info->type == OBJ_TYPE)
    sig_a.sig.imp.hit.u.obj_type = obj_getObjType(coll_info->other.obj);
  else
    sig_a.sig.imp.hit.u.tile_type = map_getTileType(obj_getObjLayer(obj), coll_info->other.tile_pos.x, coll_info->other.tile_pos.y);

  obj_sendObjSignal(obj, &sig_a);

  /* Update the 2nd object (if it's an object) */
  if (coll_info->type == OBJ_TYPE)
    {
      v_b.x += impulse_b.x;
      v_b.y += impulse_b.y;
      obj_setObjVel(coll_info->other.obj, v_b);
      obj_setObjPos(coll_info->other.obj, new_pos_b);

      /* Send an impulse signal */
      sig_b.type = IMPULSE_SIG;
      sig_b.sig.imp.vec = impulse_b;
      sig_b.sig.imp.hit.type = OBJ_TYPE;
      sig_b.sig.imp.hit.u.obj_type = obj_getObjType(obj);

      obj_sendObjSignal(coll_info->other.obj, &sig_b);
    }
  /* If it's a tile, create an impulse to send it: */
  else
    {
      sig_b.type = IMPULSE_SIG;
      sig_b.sig.imp.vec.x = -impulse_a.x;
      sig_b.sig.imp.vec.y = -impulse_a.y;
      sig_b.sig.imp.hit.type = OBJ_TYPE;
      sig_b.sig.imp.hit.u.obj_type = obj_getObjType(obj);

      map_sendTileSignal(obj_getObjLayer(obj), coll_info->other.tile_pos.x, coll_info->other.tile_pos.y, &sig_b);
    }
}
Esempio n. 8
0
bool CChar::CanSeeLOS_New( const CPointMap &ptDst, CPointMap *pptBlock, int iMaxDist, word flags, bool bCombatCheck ) const
{
	ADDTOCALLSTACK("CChar::CanSeeLOS_New");
	// WARNING: CanSeeLOS is an expensive function (lot of calculations but	most importantly it has to read the UO files, and file I/O is slow).

	if ( !bCombatCheck && IsPriv(PRIV_GM) )	// If i'm checking the LOS during a combat, i don't want to shoot through the walls even if i'm a GM
	{
		WARNLOS(("GM Pass\n"));
		return true;
	}

	CPointMap ptSrc = GetTopPoint();
	CPointMap ptNow(ptSrc);

	if ( ptSrc.m_map != ptDst.m_map )	// Different map
		return this->CanSeeLOS_New_Failed(pptBlock, ptNow);

	if ( ptSrc == ptDst )	// Same point ^^
		return true;

	short iTotalZ = ptSrc.m_z + GetHeightMount(true);
	ptSrc.m_z = (char)minimum(iTotalZ, UO_SIZE_Z);	//true - substract one from the height because of eyes height
	WARNLOS(("Total Z: %d\n", ptSrc.m_z));

	int dx, dy, dz;
	dx = ptDst.m_x - ptSrc.m_x;
	dy = ptDst.m_y - ptSrc.m_y;
	dz = ptDst.m_z - ptSrc.m_z;

    float dist2d, dist3d;
	dist2d = sqrt((float)(dx*dx + dy*dy));
	if ( dz )
		dist3d = sqrt((float)(dist2d*dist2d + dz*dz));
	else
		dist3d = dist2d;

	if ( APPROX(dist2d) > (float)iMaxDist )
	{
		WARNLOS(("( APPROX(dist2d)(%f) > ((double)iMaxDist)(%f) ) --> NOLOS\n", APPROX(dist2d), (float)iMaxDist));
		return CanSeeLOS_New_Failed(pptBlock, ptNow);
	}

	float dFactorX, dFactorY, dFactorZ;
	dFactorX = dx / dist3d;
	dFactorY = dy / dist3d;
	dFactorZ = dz / dist3d;

    float nPx, nPy, nPz;
	nPx = ptSrc.m_x;
	nPy = ptSrc.m_y;
	nPz = ptSrc.m_z;

	std::vector<CPointMap> path;
	for (;;)
	{
		if ( BETWEENPOINT(nPx, ptDst.m_x, ptSrc.m_x) && BETWEENPOINT(nPy, ptDst.m_y, ptSrc.m_y) && BETWEENPOINT(nPz, ptDst.m_z, ptSrc.m_z) )
		{
			dx = (int)APPROX(nPx);
			dy = (int)APPROX(nPy);
			dz = (int)APPROX(nPz);

			// Add point to vector
			if ( !path.empty() )
			{
				CPointMap ptEnd = path[path.size() - 1];
				if ( ptEnd.m_x != dx || ptEnd.m_y != dy || ptEnd.m_z != dz )
					path.emplace_back((word)dx, (word)dy, (char)dz, ptSrc.m_map);
			}
			else
			{
				path.emplace_back((word)dx, (word)dy, (char)dz, ptSrc.m_map);
			}
			WARNLOS(("PATH X:%d Y:%d Z:%d\n", dx, dy, dz));

			nPx += dFactorX;
			nPy += dFactorY;
			nPz += dFactorZ;
		}
		else
			break;
	}

	if ( !path.empty() )
	{
		if ( path[path.size() - 1] != ptDst )
			path.emplace_back(ptDst.m_x, ptDst.m_y, ptDst.m_z, ptDst.m_map);
	}
	else
	{
		path.clear();
		return CanSeeLOS_New_Failed(pptBlock, ptNow);
	}

	WARNLOS(("Path calculated %" PRIuSIZE_T "\n", path.size()));
	// Ok now we should loop through all the points and checking for maptile, staticx, items, multis.
	// If something is in the way and it has the wrong flags LOS return false

	const CServerMapBlock *pBlock			= nullptr;		// Block of the map (for statics)
	const CUOStaticItemRec *pStatic			= nullptr;		// Statics iterator (based on SphereMapBlock)
	const CSphereMulti *pMulti 				= nullptr;		// Multi Def (multi check)
	const CUOMultiItemRec_HS *pMultiItem	= nullptr;		// Multi item iterator
	CRegion *pRegion					= nullptr;		// Nulti regions
	CRegionLinks rlinks;								// Links to multi regions
	CItem *pItem						= nullptr;
	CItemBase *pItemDef 				= nullptr;
	CItemBaseDupe *pDupeDef				= nullptr;

	dword wTFlags = 0;
	height_t Height = 0;
	word terrainid = 0;
	bool bPath = true;
	bool bNullTerrain = false;

	CRegion *pSrcRegion = ptSrc.GetRegion(REGION_TYPE_AREA|REGION_TYPE_ROOM|REGION_TYPE_MULTI);
	CRegion *pNowRegion = nullptr;

	int lp_x = 0, lp_y = 0;
	short min_z = 0, max_z = 0;

	for (uint i = 0, pathSize = uint(path.size()); i < pathSize; lp_x = ptNow.m_x, lp_y = ptNow.m_y, pItemDef = nullptr, pStatic = nullptr, pMulti = nullptr, pMultiItem = nullptr, min_z = 0, max_z = 0, ++i )
	{
		ptNow = path[i];
		WARNLOS(("---------------------------------------------\n"));
		WARNLOS(("Point %d,%d,%d \n", ptNow.m_x, ptNow.m_y, ptNow.m_z));

		pNowRegion = ptNow.GetRegion(REGION_TYPE_AREA|REGION_TYPE_ROOM|REGION_TYPE_MULTI);

		if ( (flags & LOS_NO_OTHER_REGION) && (pSrcRegion != pNowRegion) )
		{
			WARNLOS(("flags & 0200 and path is leaving my region - BLOCK\n"));
			bPath = false;
			break;
		}

		if ( (flags & LOS_NC_MULTI) && ptNow.GetRegion(REGION_TYPE_MULTI) && (ptNow.GetRegion(REGION_TYPE_MULTI) != ptSrc.GetRegion(REGION_TYPE_MULTI)) )
		{
			WARNLOS(("flags & 0400 and path is crossing another multi - BLOCK\n"));
			bPath = false;
			break;
		}

		if ( (lp_x != ptNow.m_x) || (lp_y != ptNow.m_y) )
		{
			WARNLOS(("\tLoading new map block.\n"));
			pBlock = g_World.GetMapBlock(ptNow);
		}

		if ( !pBlock ) // something is wrong
		{
			WARNLOS(("GetMapBlock Failed\n"));
			bPath = false;
			break;
		}

		if ( !(flags & LOS_NB_TERRAIN) )
		{
			if ( !((flags & LOS_NB_LOCAL_TERRAIN) && (pSrcRegion == pNowRegion)) )
			{
				// ------ MapX.mul Check ----------
				terrainid = pBlock->GetTerrain(UO_BLOCK_OFFSET(ptNow.m_x), UO_BLOCK_OFFSET(ptNow.m_y))->m_wTerrainIndex;
				WARNLOS(("Terrain %d\n", terrainid));

				if ( (flags & LOS_FISHING) && (ptSrc.GetDist(ptNow) >= 2) && (g_World.GetTerrainItemType(terrainid) != IT_WATER) && (g_World.GetTerrainItemType(terrainid) != IT_NORMAL) )
				{
					WARNLOS(("Terrain %d blocked - flags & LOS_FISHING, distance >= 2 and type of pItemDef is not IT_WATER\n", terrainid));
					WARNLOS(("ptSrc: %d,%d,%d; ptNow: %d,%d,%d; terrainid: %d; terrainid type: %d\n", ptSrc.m_x, ptSrc.m_y, ptSrc.m_z, ptNow.m_x, ptNow.m_y, ptNow.m_z, terrainid, g_World.GetTerrainItemType(terrainid)));
					bPath = false;
					break;
				}

				//#define MAPTILEMIN minimum(minimum(minimum(pBlock->GetTerrain(0,0)->m_z, pBlock->GetTerrain(0,1)->m_z), pBlock->GetTerrain(1,0)->m_z), pBlock->GetTerrain(1,1)->m_z)
				//#define MAPTILEMAX maximum(maximum(maximum(pBlock->GetTerrain(0,0)->m_z, pBlock->GetTerrain(0,1)->m_z), pBlock->GetTerrain(1,0)->m_z), pBlock->GetTerrain(1,1)->m_z);
				//#define MAPTILEZ pBlock->GetTerrain(UO_BLOCK_OFFSET(ptNow.m_x), UO_BLOCK_OFFSET(ptNow.m_y))->m_z;

				if ( (terrainid != TERRAIN_HOLE) && (terrainid != 475) )
				{
					if ( terrainid < 430 || terrainid > 437 )
					{
						/*this stuff should do some checking for surrounding items:
						aaa
						aXa
						aaa
						min_z is determined as a minimum of all a/X terrain, where X is ptNow
						*/
						byte pos_x = UO_BLOCK_OFFSET(ptNow.m_x) > 1 ? UO_BLOCK_OFFSET(ptNow.m_x - 1) : 0;
						byte pos_y = UO_BLOCK_OFFSET(ptNow.m_y) > 1 ? UO_BLOCK_OFFSET(ptNow.m_y - 1) : 0;
						const byte defx = UO_BLOCK_OFFSET(ptNow.m_x);
						const byte defy = UO_BLOCK_OFFSET(ptNow.m_y);
						min_z = pBlock->GetTerrain(pos_x, pos_y)->m_z;
						max_z = pBlock->GetTerrain(defx, defy)->m_z;
						for ( byte posy = pos_y; (abs(defx - UO_BLOCK_OFFSET(pos_x)) <= 1 && pos_x <= 7); ++pos_x )
						{
							for ( pos_y = posy; (abs(defy - UO_BLOCK_OFFSET(pos_y)) <= 1 && pos_y <= 7); ++pos_y )
							{
								char terrain_z = pBlock->GetTerrain(pos_x, pos_y)->m_z;
								min_z = minimum(min_z, terrain_z);
							}
						}
						//min_z = MAPTILEZ;
						//max_z = MAPTILEZ;
						WARNLOS(("Terrain %d - m:%d M:%d\n", terrainid, min_z, max_z));
						if ( CUOMapMeter::IsTerrainNull(terrainid) )
							bNullTerrain = true; //what if there are some items on that hole?
						if ( (min_z <= ptNow.m_z && max_z >= ptNow.m_z) && (ptNow.m_x != ptDst.m_x || ptNow.m_y != ptDst.m_y || min_z > ptDst.m_z || max_z < ptDst.m_z) )
						{
							WARNLOS(("Terrain %d - m:%d M:%d - block\n", terrainid, min_z, max_z));
							bPath = false;
							break;
						}
						CUOTerrainInfo land(terrainid);
						if ( (land.m_flags & UFLAG1_WATER) && (flags & LOS_NC_WATER) )
							bNullTerrain = true;
					}
				}
				//#undef MAPTILEMIN
				//#undef MAPTILEMAX
				//#undef MAPTILEZ
			}
		}

		// ------ StaticsX.mul Check --------
		if ( !(flags & LOS_NB_STATIC) )
		{
			if ( !((flags & LOS_NB_LOCAL_STATIC) && (pSrcRegion == pNowRegion)) )
			{
                uint uiStaticMaxQty = pBlock->m_Statics.GetStaticQty();
				for ( uint s = 0; s < uiStaticMaxQty; pStatic = nullptr, pItemDef = nullptr, ++s )
				{
					pStatic = pBlock->m_Statics.GetStatic(s);
					if ( (pStatic->m_x + pBlock->m_x != ptNow.m_x) || (pStatic->m_y + pBlock->m_y != ptNow.m_y) )
						continue;

					//Fix for Stacked items blocking view
					if ( (pStatic->m_x == ptDst.m_x) && (pStatic->m_y == ptDst.m_y) && (pStatic->m_z >= GetTopZ()) && (pStatic->m_z <= ptSrc.m_z) )
						continue;

					pItemDef = CItemBase::FindItemBase(pStatic->GetDispID());
					wTFlags = 0;
					Height = 0;
					bNullTerrain = false;

					if ( !pItemDef )
					{
						WARNLOS(("STATIC - Cannot get pItemDef for item (0%x)\n", pStatic->GetDispID()));
					}
					else
					{
                        if (pItemDef->Can(CAN_I_BLOCKLOS))
                        {
                            WARNLOS(("pStatic blocked by CAN_I_BLOCKLOS"));
                            bPath = false;
                            break;
                        }

						if ( (flags & LOS_FISHING) && (ptSrc.GetDist(ptNow) >= 2) && (pItemDef->GetType() != IT_WATER) &&
							( pItemDef->Can(CAN_I_DOOR | CAN_I_PLATFORM | CAN_I_BLOCK | CAN_I_CLIMB | CAN_I_FIRE | CAN_I_ROOF | CAN_I_BLOCKLOS | CAN_I_BLOCKLOS_HEIGHT)) )
						{
							WARNLOS(("pStatic blocked - flags & 0800, distance >= 2 and type of pItemDef is not IT_WATER\n"));
							bPath = false;
							break;
						}

						wTFlags = pItemDef->GetTFlags();
						Height = pItemDef->GetHeight();

						if ( pItemDef->GetID() != pStatic->GetDispID() ) //not a parent item
						{
							WARNLOS(("Not a parent item (STATIC)\n"));
							pDupeDef = CItemBaseDupe::GetDupeRef((ITEMID_TYPE)(pStatic->GetDispID()));
							if ( !pDupeDef )
							{
								g_Log.EventDebug("AdvancedLoS: Failed to get non-parent reference (static) (DispID 0%x) (X: %d Y: %d Z: %hhd M: %hhu)\n", pStatic->GetDispID(), ptNow.m_x, ptNow.m_y, pStatic->m_z, ptNow.m_map);
							}
							else
							{
								wTFlags = pDupeDef->GetTFlags();
								Height = pDupeDef->GetHeight();
							}
						}
						else
						{
							WARNLOS(("Parent item (STATIC)\n"));
						}

						Height = (wTFlags & UFLAG2_CLIMBABLE) ? Height / 2 : Height;

						if ( ((wTFlags & (UFLAG1_WALL|UFLAG1_BLOCK|UFLAG2_PLATFORM)) || (pItemDef->Can(CAN_I_BLOCKLOS_HEIGHT))) && !((wTFlags & UFLAG2_WINDOW) && (flags & LOS_NB_WINDOWS)) )
						{
							WARNLOS(("pStatic %0x %d,%d,%d - %d\n", pStatic->GetDispID(), pStatic->m_x, pStatic->m_y, pStatic->m_z, Height));
							min_z = pStatic->m_z;
							max_z = minimum(Height + min_z, UO_SIZE_Z);
							WARNLOS(("wTFlags(0%x)\n", wTFlags));

							WARNLOS(("pStatic %0x Z check: %d,%d (Now: %d) (Dest: %d).\n", pStatic->GetDispID(), min_z, max_z, ptNow.m_z, ptDst.m_z));
							if ( (min_z <= ptNow.m_z) && (max_z >= ptNow.m_z) )
							{
								if ( ptNow.m_x != ptDst.m_x || ptNow.m_y != ptDst.m_y || min_z > ptDst.m_z || max_z < ptDst.m_z )
								{
									WARNLOS(("pStatic blocked - m:%d M:%d\n", min_z, max_z));
									bPath = false;
									break;
								}
							}
						}
					}
				}
			}
		}

		if ( !bPath )
			break;

		// --------- In game items ----------
		if ( !(flags & LOS_NB_DYNAMIC) )
		{
			if ( !((flags & LOS_NB_LOCAL_DYNAMIC) && (pSrcRegion == pNowRegion)) )
			{
				CWorldSearch AreaItems(ptNow, 0);
				for (;;)
				{
					pItem = AreaItems.GetItem();
					if ( !pItem )
						break;
					if ( pItem->GetUnkPoint().m_x != ptNow.m_x || pItem->GetUnkPoint().m_y != ptNow.m_y )
						continue;
					if ( !CanSeeItem(pItem) )
						continue;

					//Fix for Stacked items blocking view
					if ( (pItem->GetUnkPoint().m_x == ptDst.m_x) && (pItem->GetUnkPoint().m_y == ptDst.m_y) && (pItem->GetUnkPoint().m_z >= GetTopZ()) && (pItem->GetUnkPoint().m_z <= ptSrc.m_z) )
						continue;

					pItemDef = static_cast<CItemBase*>(pItem->Base_GetDef());
					wTFlags = 0;
					Height = 0;
					bNullTerrain = false;

					if ( !pItemDef )
					{
						WARNLOS(("DYNAMIC - Cannot get pItemDef for item (0%x)\n", pItem->GetDispID()));
					}
					else
					{
                        if (pItem->Can(CAN_I_BLOCKLOS))
                        {
                            WARNLOS(("pItem blocked by CAN_I_BLOCKLOS"));
                            bPath = false;
                            break;
                        }

						if ( (flags & LOS_FISHING) && (ptSrc.GetDist(ptNow) >= 2) && (pItem->GetType() != IT_WATER) &&
							( pItem->Can(CAN_I_DOOR | CAN_I_PLATFORM | CAN_I_BLOCK | CAN_I_CLIMB | CAN_I_FIRE | CAN_I_ROOF | CAN_I_BLOCKLOS | CAN_I_BLOCKLOS_HEIGHT) ) )
						{
							WARNLOS(("pItem blocked - flags & 0800, distance >= 2 and type of pItemDef is not IT_WATER\n"));
							bPath = false;
							break;
							//return CanSeeLOS_New_Failed(pptBlock, ptNow);
						}

						wTFlags = pItemDef->GetTFlags();
						Height = pItemDef->GetHeight();

						if ( pItemDef->GetID() != pItem->GetDispID() )	//not a parent item
						{
							WARNLOS(("Not a parent item (DYNAMIC)\n"));
							pDupeDef = CItemBaseDupe::GetDupeRef((ITEMID_TYPE)(pItem->GetDispID()));
							if ( !pDupeDef )
							{
                                // Not an error: i have changed the DISPID of the item.
                                CItemBase* pParentDef = CItemBase::FindItemBase(pItem->GetDispID());
                                if (pParentDef)
                                {
                                    wTFlags = pParentDef->GetTFlags();
                                    Height = pParentDef->GetHeight();
                                }
                                else
                                    g_Log.EventDebug("AdvancedLoS: Failed to get reference (dynamic): non-dupe, baseless dispid (DispID 0%x) (X: %d Y: %d Z: %hhd M: %hhu)\n", pItem->GetDispID(), ptNow.m_x, ptNow.m_y, pItem->GetUnkZ(), ptNow.m_map);
							}
							else
							{
                                // It's a dupe item
								wTFlags = pDupeDef->GetTFlags();
								Height = pDupeDef->GetHeight();
							}
						}
						else
						{
							WARNLOS(("Parent item (DYNAMIC)\n"));
						}

						Height = (wTFlags & UFLAG2_CLIMBABLE) ? Height / 2 : Height;

						if ( ((wTFlags & (UFLAG1_WALL|UFLAG1_BLOCK|UFLAG2_PLATFORM)) || pItem->Can(CAN_I_BLOCKLOS_HEIGHT)) && !((wTFlags & UFLAG2_WINDOW) && (flags & LOS_NB_WINDOWS)) )
						{
							WARNLOS(("pItem %0x(%0x) %d,%d,%d - %d\n", (dword)pItem->GetUID(), pItem->GetDispID(), pItem->GetUnkPoint().m_x, pItem->GetUnkPoint().m_y, pItem->GetUnkPoint().m_z, Height));
							min_z = pItem->GetUnkPoint().m_z;
							max_z = minimum(Height + min_z, UO_SIZE_Z);
							WARNLOS(("wTFlags(0%x)\n", wTFlags));

							WARNLOS(("pItem %0x(%0x) Z check: %d,%d (Now: %d) (Dest: %d).\n", (dword)pItem->GetUID(), pItem->GetDispID(), min_z, max_z, ptNow.m_z, ptDst.m_z));
							if ( min_z <= ptNow.m_z && max_z >= ptNow.m_z )
							{
								if ( ptNow.m_x != ptDst.m_x || ptNow.m_y != ptDst.m_y || min_z > ptDst.m_z || max_z < ptDst.m_z )
								{
									WARNLOS(("pItem blocked - m:%d M:%d\n", min_z, max_z));
									bPath = false;
									break;
								}
							}
						}
					}
				}
			}
		}

		if ( !bPath )
			break;

		// ----------- Multis ---------------

		if ( !(flags & LOS_NB_MULTI) )
		{
			if ( !((flags & LOS_NB_LOCAL_MULTI) && (pSrcRegion == pNowRegion)) )
			{
				size_t iQtyr = ptNow.GetRegions(REGION_TYPE_MULTI, &rlinks);
				if ( iQtyr > 0 )
				{
					for ( size_t ii = 0; ii < iQtyr; pMulti = nullptr, ++ii, pItem = nullptr, pRegion = nullptr )
					{
						pRegion = rlinks[ii];
						if ( pRegion )
							pItem = pRegion->GetResourceID().ItemFindFromResource();

						if ( !pItem )
							continue;

						pMulti = g_Cfg.GetMultiItemDefs(pItem);
						if ( !pMulti )
							continue;

						uint iQty = pMulti->GetItemCount();
						for ( uint iii = 0; iii < iQty; pItemDef = nullptr, pMultiItem = nullptr, ++iii )
						{
							pMultiItem = pMulti->GetItem(iii);
							if ( !pMultiItem )
								break;
							if ( !pMultiItem->m_visible )
								continue;
							if ( (pMultiItem->m_dx + pItem->GetTopPoint().m_x != ptNow.m_x) || (pMultiItem->m_dy + pItem->GetTopPoint().m_y != ptNow.m_y) )
								continue;

							pItemDef = CItemBase::FindItemBase(pMultiItem->GetDispID());
							wTFlags = 0;
							Height = 0;
							bNullTerrain = false;

							if ( !pItemDef )
							{
								WARNLOS(("MULTI - Cannot get pItemDef for item (0%x)\n", pMultiItem->GetDispID()));
							}
							else
							{
                                if (pItemDef->Can(CAN_I_BLOCKLOS))
                                {
                                    WARNLOS(("pMultiItem blocked by CAN_I_BLOCKLOS"));
                                    bPath = false;
                                    break;
                                }

								if ( (flags & LOS_FISHING) && (ptSrc.GetDist(ptNow) >= 2) && (pItemDef->GetType() != IT_WATER) &&
									( pItemDef->Can(CAN_I_DOOR | CAN_I_PLATFORM | CAN_I_BLOCK | CAN_I_CLIMB | CAN_I_FIRE | CAN_I_ROOF | CAN_I_BLOCKLOS | CAN_I_BLOCKLOS_HEIGHT) ) )
								{
									WARNLOS(("pMultiItem blocked - flags & 0800, distance >= 2 and type of pItemDef is not IT_WATER\n"));
									bPath = false;
									break;
									//return CanSeeLOS_New_Failed(pptBlock, ptNow);
								}

								wTFlags = pItemDef->GetTFlags();
								Height = pItemDef->GetHeight();

								if ( pItemDef->GetID() != pMultiItem->GetDispID() ) //not a parent item
								{
									WARNLOS(("Not a parent item (MULTI)\n"));
									pDupeDef = CItemBaseDupe::GetDupeRef((ITEMID_TYPE)(pMultiItem->GetDispID()));
									if ( !pDupeDef )
									{
										g_Log.EventDebug("AdvancedLoS: Failed to get non-parent reference (multi) (DispID 0%x) (X: %d Y: %d Z: %hhd M: %hhu)\n", pMultiItem->GetDispID(), ptNow.m_x, ptNow.m_y, pMultiItem->m_dz + pItem->GetTopPoint().m_z, ptNow.m_map);
									}
									else
									{
										wTFlags = pDupeDef->GetTFlags();
										Height = pDupeDef->GetHeight();
									}
								}
								else
								{
									WARNLOS(("Parent item (MULTI)\n"));
								}

								Height = (wTFlags & UFLAG2_CLIMBABLE) ? Height / 2 : Height;

								if ( ((wTFlags & (UFLAG1_WALL|UFLAG1_BLOCK|UFLAG2_PLATFORM) || pItemDef->Can(CAN_I_BLOCKLOS_HEIGHT))) && !((wTFlags & UFLAG2_WINDOW) && (flags & LOS_NB_WINDOWS)) )
								{
									WARNLOS(("pMultiItem %0x %d,%d,%d - %d\n", pMultiItem->GetDispID(), pMultiItem->m_dx, pMultiItem->m_dy, pMultiItem->m_dz, Height));
									min_z = (char)(pMultiItem->m_dz) + pItem->GetTopPoint().m_z;
									max_z = minimum(Height + min_z, UO_SIZE_Z);
									WARNLOS(("wTFlags(0%x)\n", wTFlags));

									if ( min_z <= ptNow.m_z && max_z >= ptNow.m_z )
									{
										if ( ptNow.m_x != ptDst.m_x || ptNow.m_y != ptDst.m_y || min_z > ptDst.m_z || max_z < ptDst.m_z )
										{
											WARNLOS(("pMultiItem blocked - m:%d M:%d\n", min_z, max_z));
											bPath = false;
											break;
										}
									}
								}
							}
						}

						if ( !bPath )
							break;
					}
				}
			}
		}

		if ( bNullTerrain )
			bPath = false;

		if ( !bPath )
			break;
	}

	path.clear();

	if ( !bPath )
		return CanSeeLOS_New_Failed(pptBlock, ptNow);

	return true;
}