int _cdecl main( int argc, char * argv[] ) #endif { #ifndef _WIN32 // Initialize nonblocking IO and disable readline on linux g_UnixTerminal.prepare(); #endif g_Serv.m_iExitFlag = Sphere_InitServer( argc, argv ); if ( ! g_Serv.m_iExitFlag ) { WritePidFile(); // Start the ping server, this can only be ran in a separate thread if ( IsSetEF( EF_UsePingServer ) ) g_PingServer.start(); #if !defined(_WIN32) || defined(_LIBEV) if ( g_Cfg.m_fUseAsyncNetwork != 0 ) g_NetworkEvent.start(); #endif #ifndef _MTNETWORK g_NetworkIn.onStart(); if (IsSetEF( EF_NetworkOutThread )) g_NetworkOut.start(); #else g_NetworkManager.start(); #endif bool shouldRunInThread = ( g_Cfg.m_iFreezeRestartTime > 0 ); if( shouldRunInThread ) { g_Main.start(); Sphere_MainMonitorLoop(); } else { while( !g_Serv.m_iExitFlag ) { g_Main.tick(); } } } #ifdef _WIN32 NTWindow_DeleteIcon(); #endif Sphere_ExitServer(); WritePidFile(true); return( g_Serv.m_iExitFlag ); }
bool CClient::OnRxConsole( const byte * pData, size_t iLen ) { ADDTOCALLSTACK("CClient::OnRxConsole"); // A special console version of the client. (Not game protocol) if ( !iLen || ( GetConnectType() != CONNECT_TELNET )) return false; if ( IsSetEF( EF_AllowTelnetPacketFilter ) ) { bool fFiltered = xPacketFilter(pData, iLen); if ( fFiltered ) return fFiltered; } while ( iLen -- ) { int iRet = OnConsoleKey( m_Targ_Text, *pData++, GetAccount() != NULL ); if ( ! iRet ) return false; if ( iRet == 2 ) { if ( GetAccount() == NULL ) { if ( !m_zLogin[0] ) { if ( (uint)(m_Targ_Text.GetLength()) > (CountOf(m_zLogin) - 1) ) { SysMessage("Login:\n"); } else { strcpy(m_zLogin, m_Targ_Text); SysMessage("Password:\n"); } m_Targ_Text.Empty(); } else { CSString sMsg; CAccountRef pAccount = g_Accounts.Account_Find(m_zLogin); if (( pAccount == NULL ) || ( pAccount->GetPrivLevel() < PLEVEL_Admin )) { SysMessagef("%s\n", g_Cfg.GetDefaultMsg(DEFMSG_CONSOLE_NOT_PRIV)); m_Targ_Text.Empty(); return false; } if ( LogIn(m_zLogin, m_Targ_Text, sMsg ) == PacketLoginError::Success ) { m_Targ_Text.Empty(); return OnRxConsoleLoginComplete(); } else if ( ! sMsg.IsEmpty()) { SysMessage( sMsg ); return false; } m_Targ_Text.Empty(); } return true; } else { iRet = g_Serv.OnConsoleCmd( m_Targ_Text, this ); if (g_Cfg.m_fTelnetLog && GetPrivLevel() >= g_Cfg.m_iCommandLog) g_Log.Event(LOGM_GM_CMDS, "%x:'%s' commands '%s'=%d\n", GetSocketID(), GetName(), static_cast<lpctstr>(m_Targ_Text), iRet); } } } return true; }
bool CScriptObj::r_Call( LPCTSTR pszFunction, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CGString * psVal, TRIGRET_TYPE * piRet ) { GETNONWHITESPACE( pszFunction ); int index; { int iCompareRes = -1; index = g_Cfg.m_Functions.FindKeyNear( pszFunction, iCompareRes, true ); if ( iCompareRes ) index = -1; } if ( index < 0 ) return false; CResourceNamed * pFunction = STATIC_CAST <CResourceNamed *>( g_Cfg.m_Functions[index] ); ASSERT(pFunction); CResourceLock sFunction; if ( pFunction->ResourceLock(sFunction) ) { TScriptProfiler::TScriptProfilerFunction *pFun; LONGLONG ticks, ticksend; // If functions profiler is on, search this function record and get pointer to it // if not, create the corresponding record if ( IsSetEF(EF_Script_Profiler) ) { char *pName = Str_GetTemp(); char *pSpace; // lowercase for speed, and strip arguments strcpy(pName, pszFunction); if ( pSpace = strchr(pName, ' ') ) *pSpace = 0; _strlwr(pName); if ( g_profiler.initstate != 0xf1 ) // it is not initalised { memset(&g_profiler, 0, sizeof(g_profiler)); g_profiler.initstate = (unsigned char)0xf1; // '' } for ( pFun = g_profiler.FunctionsHead; pFun != NULL; pFun = pFun->next ) { if ( !strcmp(pFun->name, pName) ) break; } // first time function called. so create a record for it if ( !pFun ) { pFun = new TScriptProfiler::TScriptProfilerFunction; memset(pFun, 0, sizeof(TScriptProfiler::TScriptProfilerFunction)); strcpy(pFun->name, pName); if ( g_profiler.FunctionsTail ) g_profiler.FunctionsTail->next = pFun; else g_profiler.FunctionsHead = pFun; g_profiler.FunctionsTail = pFun; } // prepare the informational block pFun->called++; g_profiler.called++; #ifdef _WIN32 if ( ! QueryPerformanceCounter((LARGE_INTEGER *) &ticks )) ticks = GetTickCount(); #else ticks = GetTickCount(); // our own function #endif } TRIGRET_TYPE iRet =OnTriggerRun( sFunction, TRIGRUN_SECTION_TRUE, pSrc, pArgs, IsSetEF( EF_Scripts_Ret_Strings ) ? psVal : NULL ); if ( IsSetEF(EF_Script_Profiler) ) { // update the time call information #ifdef _WIN32 if ( ! QueryPerformanceCounter((LARGE_INTEGER *) &ticksend )) ticksend = GetTickCount(); #else ticksend = GetTickCount(); // our own function #endif ticks = ticksend - ticks; pFun->total += ticks; pFun->average = (pFun->total/pFun->called); if ( pFun->max < ticks ) pFun->max = ticks; if (( pFun->min > ticks ) || ( !pFun->min )) pFun->min = ticks; g_profiler.total += ticks; } if ( psVal && !IsSetEF( EF_Scripts_Ret_Strings ) ) psVal->FormatVal( iRet ); if ( piRet ) *piRet = iRet; } return( true ); }
TRIGRET_TYPE CScriptObj::OnTriggerRun( CScript &s, TRIGRUN_TYPE trigrun, CTextConsole * pSrc, CScriptTriggerArgs * pArgs, CGString * pResult ) { // ARGS: // TRIGRUN_SECTION_SINGLE = just this 1 line. // RETURN: // TRIGRET_RET_FALSE = 0 = return and continue processing. // TRIGRET_RET_TRUE = 1 = return and handled. (halt further processing) // TRIGRET_RET_DEFAULT = 2 = if process returns nothing specifically. // CScriptFileContext set g_Log.m_pObjectContext is the current context (we assume) // DEBUGCHECK( this == g_Log.m_pObjectContext ); static LPCTSTR const m_ExcKeys[] = { "parsing", "parsing IF statement", "parsing begin/loop cycle", "foritem", "forchar", "forclients", "forobjs", "forplayers", "for", "while", "forcharlayer/memorytype", "forcont", "forcontid/type", "dorand/doswitch", "return", "CALLing a subfunction", "VERBing a value", }; #ifdef WIN32 EXC_TRY(("OnTriggerRun(%x,%i,%x,%x,%x)", s.GetKey(), trigrun, pSrc, pArgs, pResult)); #else EXC_TRY(("OnTriggerRun(??,%i,%x,%x,%x)", trigrun, pSrc, pArgs, pResult)); #endif bool fSectionFalse = (trigrun == TRIGRUN_SECTION_FALSE || trigrun == TRIGRUN_SINGLE_FALSE); if ( trigrun == TRIGRUN_SECTION_EXEC || trigrun == TRIGRUN_SINGLE_EXEC ) // header was already read in. goto jump_in; EXC_SET(m_ExcKeys[0]); while ( s.ReadKeyParse()) { // Hit the end of the next trigger. if ( s.IsKeyHead( "ON", 2 )) // done with this section. break; jump_in: SK_TYPE iCmd = (SK_TYPE) FindTableSorted( s.GetKey(), sm_szScriptKeys, COUNTOF( sm_szScriptKeys )-1 ); TRIGRET_TYPE iRet; switch ( iCmd ) { case SK_ENDIF: case SK_END: case SK_ENDDO: case SK_ENDFOR: case SK_ENDRAND: case SK_ENDSWITCH: case SK_ENDWHILE: return( TRIGRET_ENDIF ); case SK_ELIF: case SK_ELSEIF: return( TRIGRET_ELSEIF ); case SK_ELSE: return( TRIGRET_ELSE ); } if ( fSectionFalse ) { // Ignoring this whole section. don't bother parsing it. switch ( iCmd ) { case SK_IF: EXC_SET(m_ExcKeys[1]); do { iRet = OnTriggerRun( s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult ); } while ( iRet == TRIGRET_ELSEIF || iRet == TRIGRET_ELSE ); break; case SK_WHILE: case SK_FOR: case SK_FORCHARLAYER: case SK_FORCHARMEMORYTYPE: case SK_FORCHAR: case SK_FORCLIENTS: case SK_FORCONT: case SK_FORCONTID: case SK_FORCONTTYPE: case SK_FORITEM: case SK_FOROBJ: case SK_FORPLAYERS: case SK_DORAND: case SK_DOSWITCH: case SK_BEGIN: EXC_SET(m_ExcKeys[2]); iRet = OnTriggerRun( s, TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult ); break; } if ( trigrun >= TRIGRUN_SINGLE_EXEC ) return( TRIGRET_RET_DEFAULT ); continue; // just ignore it. } switch ( iCmd ) { case SK_FORITEM: EXC_SET(m_ExcKeys[3]); iRet = OnTriggerForLoop( s, 1, pSrc, pArgs, pResult ); break; case SK_FORCHAR: EXC_SET(m_ExcKeys[4]); iRet = OnTriggerForLoop( s, 2, pSrc, pArgs, pResult ); break; case SK_FORCLIENTS: EXC_SET(m_ExcKeys[5]); iRet = OnTriggerForLoop( s, 0x12, pSrc, pArgs, pResult ); break; case SK_FOROBJ: EXC_SET(m_ExcKeys[6]); iRet = OnTriggerForLoop( s, 3, pSrc, pArgs, pResult ); break; case SK_FORPLAYERS: EXC_SET(m_ExcKeys[7]); iRet = OnTriggerForLoop( s, 0x22, pSrc, pArgs, pResult ); break; case SK_FOR: EXC_SET(m_ExcKeys[8]); iRet = OnTriggerForLoop( s, 4, pSrc, pArgs, pResult ); break; case SK_WHILE: EXC_SET(m_ExcKeys[9]); iRet = OnTriggerForLoop( s, 8, pSrc, pArgs, pResult ); break; case SK_FORCHARLAYER: case SK_FORCHARMEMORYTYPE: { EXC_SET(m_ExcKeys[10]); CChar * pCharThis = dynamic_cast <CChar *> (this); if ( pCharThis ) { if ( s.HasArgs() ) { if ( iCmd == SK_FORCHARLAYER ) iRet = pCharThis->OnCharTrigForLayerLoop( s, pSrc, pArgs, pResult, (LAYER_TYPE) s.GetArgVal() ); else iRet = pCharThis->OnCharTrigForMemTypeLoop( s, pSrc, pArgs, pResult, s.GetArgVal() ); break; } } } case SK_FORCONT: { EXC_SET(m_ExcKeys[11]); if ( s.HasArgs() ) { TCHAR * ppArgs[2]; TCHAR * tempPoint; TCHAR *porigValue = Str_GetTemp(); int iArgQty = Str_ParseCmds( (TCHAR*) s.GetArgRaw(), ppArgs, COUNTOF(ppArgs), " \t," ); if ( iArgQty >= 1 ) { strcpy(porigValue, ppArgs[0]); tempPoint = porigValue; ParseText( tempPoint, pSrc, 0, pArgs ); CGrayUID pCurUid = (DWORD) Exp_GetVal(tempPoint); if ( pCurUid.IsValidUID() ) { CObjBase * pObj = pCurUid.ObjFind(); if ( pObj && pObj->IsContainer() ) { CContainer * pContThis = dynamic_cast <CContainer *> (pObj); CScriptLineContext StartContext = s.GetContext(); CScriptLineContext EndContext = StartContext; iRet = pContThis->OnGenericContTriggerForLoop( s, pSrc, pArgs, pResult, StartContext, EndContext, ppArgs[1] != NULL ? Exp_GetVal(ppArgs[1]) : 255 ); break; } } } } } case SK_FORCONTID: case SK_FORCONTTYPE: { EXC_SET(m_ExcKeys[12]); CContainer * pCont = dynamic_cast <CContainer *> (this); if ( pCont ) { if ( s.HasArgs() ) { LPCTSTR pszKey = s.GetArgRaw(); SKIP_SEPERATORS(pszKey); TCHAR * ppArgs[2]; Str_ParseCmds( (TCHAR*) pszKey, ppArgs, COUNTOF(ppArgs), " \t," ); CScriptLineContext StartContext = s.GetContext(); CScriptLineContext EndContext = StartContext; #ifdef _WIN32 iRet = pCont->OnContTriggerForLoop( s, pSrc, pArgs, pResult, StartContext, EndContext, g_Cfg.ResourceGetID( ( iCmd == SK_FORCONTID ) ? RES_ITEMDEF : RES_TYPEDEF, ppArgs[0] ), 0, ppArgs[1] != NULL ? Exp_GetVal( ppArgs[1] ) : 255 ); #else iRet = pCont->OnContTriggerForLoop( s, pSrc, pArgs, pResult, StartContext, EndContext, g_Cfg.ResourceGetID( ( iCmd == SK_FORCONTID ) ? RES_ITEMDEF : RES_TYPEDEF, (const char*&) ppArgs[0] ), 0, ppArgs[1] != NULL ? Exp_GetVal( ppArgs[1] ) : 255 ); #endif break; } } } default: // Parse out any variables in it. (may act like a verb sometimes?) EXC_SET(m_ExcKeys[0]); ParseText( s.GetArgRaw(), pSrc, 0, pArgs ); } switch ( iCmd ) { case SK_FORITEM: case SK_FORCHAR: case SK_FORCHARLAYER: case SK_FORCHARMEMORYTYPE: case SK_FORCLIENTS: case SK_FORCONT: case SK_FORCONTID: case SK_FORCONTTYPE: case SK_FOROBJ: case SK_FORPLAYERS: case SK_FOR: case SK_WHILE: if ( iRet != TRIGRET_ENDIF ) { if ( iRet > TRIGRET_RET_DEFAULT ) { DEBUG_MSG(( "WARNING: Trigger Bad For Ret %d '%s','%s'\n", iRet, s.GetKey(), s.GetArgStr())); } return( iRet ); } break; case SK_DORAND: // Do a random line in here. case SK_DOSWITCH: { EXC_SET(m_ExcKeys[13]); int iVal = s.GetArgVal(); if ( iCmd == SK_DORAND ) iVal = Calc_GetRandVal(iVal); for ( ;true; iVal-- ) { iRet = OnTriggerRun( s, (!iVal) ? TRIGRUN_SINGLE_TRUE : TRIGRUN_SINGLE_FALSE, pSrc, pArgs, pResult ); if ( iRet == TRIGRET_RET_DEFAULT ) continue; if ( iRet == TRIGRET_ENDIF ) break; if ( iRet > TRIGRET_RET_DEFAULT ) { DEBUG_MSG(( "WARNING: Trigger Bad Ret %d '%s','%s'\n", iRet, s.GetKey(), s.GetArgStr())); } return( iRet ); } } break; case SK_RETURN: // Process the trigger. EXC_SET(m_ExcKeys[14]); if ( pResult ) { pResult->Copy( s.GetArgStr() ); return (TRIGRET_TYPE) 1; } return ( (TRIGRET_TYPE) s.GetArgVal() ); case SK_IF: { EXC_SET(m_ExcKeys[1]); bool fTrigger = s.GetArgVal() ? true : false; bool fBeenTrue = false; while (true) { iRet = OnTriggerRun( s, fTrigger ? TRIGRUN_SECTION_TRUE : TRIGRUN_SECTION_FALSE, pSrc, pArgs, pResult ); if ( iRet < TRIGRET_ENDIF ) return( iRet ); if ( iRet == TRIGRET_ENDIF ) break; fBeenTrue |= fTrigger; if ( fBeenTrue ) fTrigger = false; else if ( iRet == TRIGRET_ELSE ) fTrigger = true; else if ( iRet == TRIGRET_ELSEIF ) { ParseText( s.GetArgStr(), pSrc, 0, pArgs ); fTrigger = s.GetArgVal() ? true : false; } } } break; case SK_BEGIN: // Do this block here. { EXC_SET(m_ExcKeys[2]); iRet = OnTriggerRun( s, TRIGRUN_SECTION_TRUE, pSrc, pArgs, pResult ); if ( iRet != TRIGRET_ENDIF ) { if ( iRet > TRIGRET_RET_DEFAULT ) { DEBUG_MSG(( "WARNING: Trigger Bad Ret %d '%s','%s'\n", iRet, s.GetKey(), s.GetArgStr())); } return( iRet ); } } break; default: if ( IsSetEF( EF_Scripts_Parse_Verbs ) ) { EXC_SET(m_ExcKeys[0]); ParseText( s.GetKeyBuffer(), pSrc, 0, pArgs ); } EXC_SET(m_ExcKeys[0]); if ( pArgs && pArgs->r_Verb( s, pSrc ) ) ; else { bool fRes; if ( !strcmpi( (char *)s.GetKey(), "call" ) ) { EXC_SET(m_ExcKeys[15]); LPCTSTR pszArgs = strchr( s.GetArgRaw(), ' ' ); if ( pszArgs ) GETNONWHITESPACE( pszArgs ); if ( !pszArgs || !*pszArgs ) { fRes = this->r_Call( s.GetArgRaw(), pSrc, pArgs ); } else { CScriptTriggerArgs Args( pszArgs ); if ( pArgs ) Args.m_VarsLocal = pArgs->m_VarsLocal; fRes = this->r_Call( s.GetArgRaw(), pSrc, &Args ); if ( pArgs ) pArgs->m_VarsLocal = Args.m_VarsLocal; } } else { EXC_SET(m_ExcKeys[16]); fRes = r_Verb( s, pSrc ); } if ( !fRes ) { DEBUG_MSG(( "WARNING: Trigger Bad Verb '%s','%s'\n", s.GetKey(), s.GetArgStr())); } } break; } if ( trigrun >= TRIGRUN_SINGLE_EXEC ) return( TRIGRET_RET_DEFAULT ); } EXC_CATCH("running trigger line"); return( TRIGRET_RET_DEFAULT ); }
TRIGRET_TYPE CScriptObj::OnTriggerScript( CScript & s, LPCTSTR pszTrigName, CTextConsole * pSrc, CScriptTriggerArgs * pArgs ) { // look for exact trigger matches if ( ! OnTriggerFind( s, pszTrigName )) return( TRIGRET_RET_DEFAULT ); PROFILE_TYPE prvProfileTask = g_Serv.m_Profile.GetCurrentTask(); g_Serv.m_Profile.Start( PROFILE_SCRIPTS ); TScriptProfiler::TScriptProfilerTrigger *pTrig; LONGLONG ticks, ticksend; // If script profiler is on, search this trigger record and get pointer to it // if not, create the corresponding record if ( IsSetEF(EF_Script_Profiler) ) { char *pName = Str_GetTemp(); // lowercase for speed strcpy(pName, pszTrigName); _strlwr(pName); if ( g_profiler.initstate != 0xf1 ) // it is not initalised { memset(&g_profiler, 0, sizeof(g_profiler)); g_profiler.initstate = (unsigned char)0xf1; // '' } for ( pTrig = g_profiler.TriggersHead; pTrig != NULL; pTrig = pTrig->next ) { if ( !strcmp(pTrig->name, pName) ) break; } // first time function called. so create a record for it if ( !pTrig ) { pTrig = new TScriptProfiler::TScriptProfilerTrigger; memset(pTrig, 0, sizeof(TScriptProfiler::TScriptProfilerTrigger)); strcpy(pTrig->name, pName); if ( g_profiler.TriggersTail ) g_profiler.TriggersTail->next = pTrig; else g_profiler.TriggersHead = pTrig; g_profiler.TriggersTail = pTrig; } // prepare the informational block pTrig->called++; g_profiler.called++; #ifdef _WIN32 if ( ! QueryPerformanceCounter((LARGE_INTEGER *) &ticks )) ticks = GetTickCount(); #else ticks = GetTickCount(); // our own function #endif } TRIGRET_TYPE iRet; if ( !pArgs ) // all scripts should have an args block. { CScriptTriggerArgs Args( 0, 0, NULL ); iRet = OnTriggerRun( s, TRIGRUN_SECTION_TRUE, pSrc, &Args ); } else iRet = OnTriggerRun( s, TRIGRUN_SECTION_TRUE, pSrc, pArgs ); if ( IsSetEF(EF_Script_Profiler) ) { // update the time call information #ifdef _WIN32 if ( ! QueryPerformanceCounter((LARGE_INTEGER *) &ticksend )) ticksend = GetTickCount(); #else ticksend = GetTickCount(); // our own function #endif ticks = ticksend - ticks; pTrig->total += ticks; pTrig->average = (pTrig->total/pTrig->called); if ( pTrig->max < ticks ) pTrig->max = ticks; if (( pTrig->min > ticks ) || ( !pTrig->min )) pTrig->min = ticks; g_profiler.total += ticks; } g_Serv.m_Profile.Start( prvProfileTask ); return iRet; }
bool CScriptTriggerArgs::r_WriteVal( LPCTSTR pszKey, CGString &sVal, CTextConsole * pSrc ) { EXC_TRY(("r_WriteVal('%s',,%x)", pszKey, pSrc)); if ( IsSetEF( EF_Intrinsic_Locals ) ) { CVarDefBase * pVar = m_VarsLocal.GetKey( pszKey ); if ( pVar ) { sVal = pVar->GetValStr(); return true; } } else if ( !strnicmp( "LOCAL.", pszKey, 6 ) ) { pszKey += 6; sVal = m_VarsLocal.GetKeyStr( pszKey, true ); return( true ); } if ( !strnicmp( pszKey, "ARGV", 4 )) { pszKey+=4; SKIP_SEPERATORS(pszKey); int iQty = m_v.GetCount(); if ( iQty == 0 ) { // PARSE IT HERE TCHAR * pszArg = m_s1_raw.GetBuffer(); TCHAR * s = pszArg; bool fQuotes = false; while ( *s ) { if ( isspace(*s ) ) { s++; continue; } if ( *s == '"' ) { s++; fQuotes = true; }; pszArg = s; // arg starts here s++; while (*s) { if ( *s == '"' ) { if ( fQuotes ) { *s = '\0'; fQuotes = false; break; } *s = '\0'; s++; fQuotes = true; // maintain break; } if ( !fQuotes && (*s == ',') ) { *s = '\0'; s++; break; } s++; } m_v.Add( pszArg ); } iQty = m_v.GetCount(); } if ( *pszKey == '\0' ) { sVal.FormatVal(iQty); return( true ); } int iNum = Exp_GetSingle( pszKey ); SKIP_SEPERATORS(pszKey); if ( !m_v.IsValidIndex(iNum) ) { sVal.Format( "" ); return true; } sVal.Format( m_v.GetAt(iNum) ); return( true ); } int index = FindTableSorted( pszKey, sm_szLoadKeys, COUNTOF( sm_szLoadKeys )-1 ); switch (index) { case AGC_N: case AGC_N1: sVal.FormatVal( m_iN1 ); break; case AGC_N2: sVal.FormatVal( m_iN2 ); break; case AGC_N3: sVal.FormatVal( m_iN3 ); break; case AGC_S: sVal = m_s1; break; default: return( CScriptObj::r_WriteVal( pszKey, sVal, pSrc )); } return true; EXC_CATCH("CScriptTriggerArgs"); return false; }
bool CChar::CanSeeLOS( const CPointMap &ptDst, CPointMap *pptBlock, int iMaxDist, word wFlags, bool bCombatCheck ) const { ADDTOCALLSTACK("CChar::CanSeeLOS"); // 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 ( (m_pPlayer && (g_Cfg.m_iAdvancedLos & ADVANCEDLOS_PLAYER)) || (m_pNPC && (g_Cfg.m_iAdvancedLos & ADVANCEDLOS_NPC)) ) return CanSeeLOS_New(ptDst, pptBlock, iMaxDist, wFlags); // Max distance of iMaxDist // Line of sight check // NOTE: if not blocked. pptBlock is undefined. // 3D LOS later - real LOS, i.e. we can't shoot through the floor, but can shoot through the hole in it 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 return true; CPointMap ptSrc = GetTopPoint(); int iDist = ptSrc.GetDist(ptDst); if ( iDist > iMaxDist ) { blocked: if ( pptBlock ) *pptBlock = ptSrc; return false; // blocked } // Walk towards the object. If any spot is too far above our heads then we can not see what's behind it. int iDistTry = 0; while ( --iDist >= 0 ) { DIR_TYPE dir = ptSrc.GetDir(ptDst); dword dwBlockFlags; if ( dir % 2 && !IsSetEF(EF_NoDiagonalCheckLOS) ) // test only diagonal dirs { CPointMap ptTest = ptSrc; DIR_TYPE dirTest1 = (DIR_TYPE)(dir - 1); // get 1st ortogonal DIR_TYPE dirTest2 = (DIR_TYPE)(dir + 1); // get 2nd ortogonal if ( dirTest2 == DIR_QTY ) // roll over dirTest2 = DIR_N; ptTest.Move(dirTest1); dwBlockFlags = CAN_C_SWIM|CAN_C_WALK|CAN_C_FLY; char z = g_World.GetHeightPoint2(ptTest, dwBlockFlags, true); short zDiff = (short)(abs(z - ptTest.m_z)); if ( (zDiff > PLAYER_HEIGHT) || (dwBlockFlags & (CAN_I_BLOCK|CAN_I_DOOR)) ) // blocked { ptTest = ptSrc; ptTest.Move(dirTest2); { dwBlockFlags = CAN_C_SWIM|CAN_C_WALK|CAN_C_FLY; z = g_World.GetHeightPoint2(ptTest, dwBlockFlags, true); zDiff = (short)(abs(z - ptTest.m_z)); if ( zDiff > PLAYER_HEIGHT ) goto blocked; if ( dwBlockFlags & (CAN_I_BLOCK|CAN_I_DOOR) ) { ptSrc = ptTest; goto blocked; } } } ptTest.m_z = z; } if ( iDist ) { ptSrc.Move(dir); // NOTE: The dir is very coarse and can change slightly. dwBlockFlags = CAN_C_SWIM|CAN_C_WALK|CAN_C_FLY; char z = g_World.GetHeightPoint2(ptSrc, dwBlockFlags, true); short zDiff = (short)(abs(z - ptSrc.m_z)); if ( (zDiff > PLAYER_HEIGHT) || (dwBlockFlags & (CAN_I_BLOCK|CAN_I_DOOR)) || (iDistTry > iMaxDist) ) goto blocked; ptSrc.m_z = z; ++iDistTry; } } if ( abs(ptSrc.m_z - ptDst.m_z) >= 20 ) return false; return true; // made it all the way to the object with no obstructions. }