int CItemMulti::Ship_ListObjs( CObjBase ** ppObjList ) { // List all the objects in the structure. // Move the ship and everything on the deck // If too much stuff. then some will fall overboard. hehe. if ( ! IsTopLevel()) return 0; int iMaxDist = Multi_GetMaxDist(); // always list myself first. All other items must see my new region ! int iCount = 0; ppObjList[iCount++] = this; CWorldSearch AreaChar( GetTopPoint(), iMaxDist ); while ( iCount < MAX_MULTI_LIST_OBJS ) { CChar * pChar = AreaChar.GetChar(); if ( pChar == NULL ) break; if ( pChar->IsClient()) { pChar->GetClient()->addPause(); // get rid of flicker. for anyone even seeing this. } if ( ! m_pRegion->IsInside2d( pChar->GetTopPoint())) continue; int zdiff = pChar->GetTopZ() - GetTopZ(); if ( abs( zdiff ) > 3 ) continue; ppObjList[iCount++] = pChar; } CWorldSearch AreaItem( GetTopPoint(), iMaxDist ); while ( iCount < MAX_MULTI_LIST_OBJS ) { CItem * pItem = AreaItem.GetItem(); if ( pItem == NULL ) break; if ( pItem == this ) // already listed. continue; if ( ! Multi_IsPartOf( pItem )) { if ( ! m_pRegion->IsInside2d( pItem->GetTopPoint())) continue; if ( ! pItem->IsMovable()) continue; int zdiff = pItem->GetTopZ() - GetTopZ(); if ( abs( zdiff ) > 3 ) continue; } ppObjList[iCount++] = pItem; } return( iCount ); }
bool CItemMulti::Ship_MoveDelta( CPointBase pdelta ) { // Move the ship one space in some direction. ASSERT( m_pRegion->m_iLinkedSectors ); int znew = GetTopZ() + pdelta.m_z; if ( pdelta.m_z > 0 ) { if ( znew >= (UO_SIZE_Z - PLAYER_HEIGHT )-1 ) return( false ); } else if ( pdelta.m_z < 0 ) { if ( znew <= (UO_SIZE_MIN_Z + 3 )) return( false ); } // Move the ship and everything on the deck CObjBase * ppObjs[MAX_MULTI_LIST_OBJS+1]; int iCount = Ship_ListObjs( ppObjs ); for ( int i=0; i <iCount; i++ ) { CObjBase * pObj = ppObjs[i]; ASSERT(pObj); CPointMap pt = pObj->GetTopPoint(); pt += pdelta; if ( ! pt.IsValidPoint()) // boat goes out of bounds ! { DEBUG_ERR(( "Ship uid=0%x out of bounds\n", (DWORD) GetUID())); continue; } pObj->MoveTo( pt ); if ( pObj->IsChar()) { ASSERT( m_pRegion->m_iLinkedSectors ); pObj->RemoveFromView(); // Get rid of the blink/walk pObj->Update(); } } return( true ); }
bool CItemMulti::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command from script { LOCKDATA; EXC_TRY(("r_Verb('%s %s',%x)", s.GetKey(), s.GetArgStr(), pSrc)); // Speaking in this ships region. // return: true = command for the ship. //"One (direction*)", " (Direction*), one" Moves ship one tile in desired direction and stops. //"Slow (direction*)" Moves ship slowly in desired direction (see below for possible directions). int iCmd = FindTableSorted( s.GetKey(), sm_szVerbKeys, COUNTOF( sm_szVerbKeys )-1 ); if ( iCmd < 0 ) { return( CItem::r_Verb( s, pSrc )); } if ( ! pSrc ) return( false ); switch ( iCmd ) { case SHV_MULTICREATE: { CGrayUID uid( s.GetArgVal() ); CChar * pSrc = uid.CharFind(); Multi_Create( pSrc, 0 ); } return true; } if ( IsAttr(ATTR_MOVE_NEVER) || ! IsTopLevel() ) return false; CChar * pChar = pSrc->GetChar(); // Only key holders can command the ship ??? // if ( pChar && pChar->ContentFindKeyFor( pItem )) // Find the tiller man object. CItem * pTiller = Multi_GetSign(); ASSERT( pTiller ); // Get current facing dir. DIR_TYPE DirFace = sm_Ship_FaceDir[ Ship_GetFaceOffset() ]; int DirMoveChange; LPCTSTR pszSpeak = NULL; switch ( iCmd ) { case SHV_SHIPSTOP: // "Furl sail" // "Stop" Stops current ship movement. if ( ! m_itShip.m_fSail ) return( false ); Ship_Stop(); break; case SHV_SHIPFACE: // Face this direction. do not change the direction of movement. if ( ! s.HasArgs()) return( false ); return Ship_Face( GetDirStr( s.GetArgStr())); case SHV_SHIPMOVE: // Move one space in this direction. // Does NOT protect against exploits ! if ( ! s.HasArgs()) return( false ); m_itShip.m_DirMove = GetDirStr( s.GetArgStr()); return Ship_Move( (DIR_TYPE) m_itShip.m_DirMove ); case SHV_SHIPGATE: // Move the whole ship and contents to another place. if ( ! s.HasArgs()) return( false ); { CPointMap ptdelta = g_Cfg.GetRegionPoint( s.GetArgStr()); if ( ! ptdelta.IsValidPoint()) return( false ); ptdelta -= GetTopPoint(); return Ship_MoveDelta( ptdelta ); } break; case SHV_SHIPTURNLEFT: // "Port", // "Turn Left", DirMoveChange = -2; doturn: if ( m_itShip.m_fAnchored ) { anchored: pszSpeak = "The anchor is down <SEX Sir/Mam>!"; break; } m_itShip.m_DirMove = GetDirTurn( DirFace, DirMoveChange ); Ship_Face( (DIR_TYPE) m_itShip.m_DirMove ); break; case SHV_SHIPTURNRIGHT: // "Turn Right", // "Starboard", // Turn Right DirMoveChange = 2; goto doturn; case SHV_SHIPDRIFTLEFT: // "Left", // "Drift Left", DirMoveChange = -2; dodirmovechange: if ( m_itShip.m_fAnchored ) goto anchored; if ( ! Ship_SetMoveDir( GetDirTurn( DirFace, DirMoveChange ))) return( false ); break; case SHV_SHIPDRIFTRIGHT: // "Right", // "Drift Right", DirMoveChange = 2; goto dodirmovechange; case SHV_SHIPBACK: // "Back", // Move ship backwards // "Backward", // Move ship backwards // "Backwards", // Move ship backwards DirMoveChange = 4; goto dodirmovechange; case SHV_SHIPFORE: // "Forward" // "Foreward", // Moves ship forward. // "Unfurl sail", // Moves ship forward. DirMoveChange = 0; goto dodirmovechange; case SHV_SHIPFORELEFT: // "Forward left", DirMoveChange = -1; goto dodirmovechange; case SHV_SHIPFORERIGHT: // "forward right", DirMoveChange = 1; goto dodirmovechange; case SHV_SHIPBACKLEFT: // "backward left", // "back left", DirMoveChange = -3; goto dodirmovechange; case SHV_SHIPBACKRIGHT: // "backward right", // "back right", DirMoveChange = 3; goto dodirmovechange; case SHV_SHIPANCHORRAISE: // "Raise Anchor", if ( ! m_itShip.m_fAnchored ) { pszSpeak = "The anchor is already up <SEX Sir/Mam>"; break; } m_itShip.m_fAnchored = false; break; case SHV_SHIPANCHORDROP: // "Drop Anchor", if ( m_itShip.m_fAnchored ) { pszSpeak = "The anchor is already down <SEX Sir/Mam>"; break; } m_itShip.m_fAnchored = true; Ship_Stop(); break; case SHV_SHIPTURN: // "Turn around", // Turns ship around and proceeds. // "Come about", // Turns ship around and proceeds. DirMoveChange = 4; goto doturn; case SHV_SHIPUP: // "Up" { if ( ! IsAttr(ATTR_MAGIC )) return( false ); CPointMap pt; pt.m_z = PLAYER_HEIGHT; if ( Ship_MoveDelta( pt )) { pszSpeak = "As you command <SEX Sir/Mam>"; } else { pszSpeak = "Can't do that <SEX Sir/Mam>"; } } break; case SHV_SHIPDOWN: // "Down" { if ( ! IsAttr(ATTR_MAGIC )) return( false ); CPointMap pt; pt.m_z = -PLAYER_HEIGHT; if ( Ship_MoveDelta( pt )) { pszSpeak = "As you command <SEX Sir/Mam>"; } else { pszSpeak = "Can't do that <SEX Sir/Mam>"; } } break; case SHV_SHIPLAND: // "Land" { if ( ! IsAttr(ATTR_MAGIC )) return( false ); signed char zold = GetTopZ(); CPointMap pt = GetTopPoint(); pt.m_z = zold; SetTopZ( -UO_SIZE_Z ); // bottom of the world where i won't get in the way. WORD wBlockFlags = CAN_I_WATER; signed char z = g_World.GetHeightPoint( pt, wBlockFlags ); SetTopZ( zold ); // restore z for now. pt.InitPoint(); pt.m_z = z - zold; if ( pt.m_z ) { Ship_MoveDelta( pt ); pszSpeak = "As you command <SEX Sir/Mam>"; } else { pszSpeak = "We have landed <SEX Sir/Mam>"; } } break; default: return( false ); } if ( pChar ) { if ( pszSpeak == NULL ) { static LPCTSTR const sm_pszAye[] = { "Aye", "Aye Cap'n", "Aye <SEX Sir/Mam>", }; pszSpeak = sm_pszAye[ Calc_GetRandVal( COUNTOF( sm_pszAye )) ]; } TCHAR szText[ MAX_TALK_BUFFER ]; strcpy( szText, pszSpeak ); pChar->ParseText( szText, &g_Serv ); pTiller->Speak( szText, 0, TALKMODE_SAY, FONT_NORMAL ); } return false; EXC_CATCH("CItemMulti"); return true; }
bool CItemMulti::Ship_Move( DIR_TYPE dir ) { if ( dir >= DIR_QTY ) return( false ); if ( m_pRegion == NULL ) { DEBUG_ERR(( "Ship bad region\n" )); return false; } CPointMap ptDelta; ptDelta.ZeroPoint(); ptDelta.Move( dir ); CPointMap ptForePrv = m_pRegion->GetRegionCorner(dir); CPointMap ptFore = ptForePrv; ptFore.Move( dir ); ptFore.m_z = GetTopZ(); if ( ! ptFore.IsValidPoint() || ( ptForePrv.m_x < UO_SIZE_X_REAL && ptFore.m_x >= UO_SIZE_X_REAL && ( ptFore.m_map <= 1 ))) { // Circle the globe // Fall off edge of world ? CItem * pTiller = Multi_GetSign(); ASSERT(pTiller); pTiller->Speak( "Turbulent waters Cap'n", 0, TALKMODE_SAY, FONT_NORMAL ); // Compute the new safe place to go. (where we will not overlap) int iMaxDist = Multi_GetMaxDist(); /* if ( ptFore.m_x <= 0 ) { } else if ( ptFore.m_x >= UO_SIZE_X_REAL ) { } else if ( ptFore.m_y <= 0 ) { } else if ( ptFore.m_y >= UO_SIZE_Y ) { } */ return false; } // We should check all relevant corners. if ( ! Ship_CanMoveTo( ptFore )) { cantmove: CItem * pTiller = Multi_GetSign(); ASSERT(pTiller); pTiller->Speak( "We've stopped Cap'n", 0, TALKMODE_SAY, FONT_NORMAL ); return false; } // left side CPointMap ptTmp = m_pRegion->GetRegionCorner(GetDirTurn(dir,-1)); ptTmp.Move( dir ); ptTmp.m_z = GetTopZ(); if ( ! Ship_CanMoveTo( ptTmp )) goto cantmove; // right side. ptTmp = m_pRegion->GetRegionCorner(GetDirTurn(dir,+1)); ptTmp.Move( dir ); ptTmp.m_z = GetTopZ(); if ( ! Ship_CanMoveTo( ptTmp )) goto cantmove; Ship_MoveDelta( ptDelta ); // Move again GetTopSector()->SetSectorWakeStatus(); // may get here b4 my client does. return true; }
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; }