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