void CSector::RespawnDeadNPCs() { ADDTOCALLSTACK("CSector::RespawnDeadNPCs"); // skip sectors in unsupported maps if ( !g_MapList.m_maps[m_map] ) return; // Respawn dead NPC's CChar * pCharNext; CChar * pChar = STATIC_CAST <CChar *>( m_Chars_Disconnect.GetHead()); for ( ; pChar != NULL; pChar = pCharNext ) { pCharNext = pChar->GetNext(); if ( ! pChar->m_pNPC ) continue; if ( ! pChar->m_ptHome.IsValidPoint()) continue; if ( ! pChar->IsStatFlag( STATF_DEAD )) continue; // Restock them with npc stuff. pChar->NPC_LoadScript(true); // Res them back to their "home". int iDist = pChar->m_pNPC->m_Home_Dist_Wander; pChar->MoveNear( pChar->m_ptHome, ( iDist < SHRT_MAX ) ? iDist : 4 ); pChar->NPC_CreateTrigger(); //Removed from NPC_LoadScript() and triggered after char placement pChar->Spell_Resurrection(); } }
void CSector::SetLightNow( bool fFlash ) { ADDTOCALLSTACK("CSector::SetLightNow"); // Set the light level for all the CClients here. CChar * pChar = STATIC_CAST <CChar*>( m_Chars_Active.GetHead()); for ( ; pChar != NULL; pChar = pChar->GetNext()) { if ( pChar->IsStatFlag( STATF_DEAD | STATF_NightSight )) continue; if ( pChar->IsClient()) { CClient * pClient = pChar->GetClient(); ASSERT(pClient); if ( fFlash ) // This does not seem to work predicably ! { BYTE bPrvLight = m_Env.m_Light; m_Env.m_Light = LIGHT_BRIGHT; // full bright. pClient->addLight(); m_Env.m_Light = bPrvLight; // back to previous. } pClient->addLight(); } // don't fire trigger when server is loading or light is flashing if (( ! g_Serv.IsLoading() && fFlash == false ) && ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) )) { pChar->OnTrigger( CTRIG_EnvironChange, pChar ); } } }
CChar *CItemCorpse::IsCorpseSleeping() const { ADDTOCALLSTACK("CItemCorpse::IsCorpseSleeping"); // Is this corpse really a sleeping person ? // CItemCorpse if ( !IsType(IT_CORPSE) ) { DEBUG_ERR(("Corpse (0%x) doesn't have type T_CORPSE! (it has %d)\n", (dword)GetUID(), GetType())); return NULL; } CChar *pCharCorpse = m_uidLink.CharFind(); if ( pCharCorpse && pCharCorpse->IsStatFlag(STATF_Sleeping) && !GetTimeStamp().IsTimeValid() ) return pCharCorpse; return NULL; }
bool CClient::Cmd_Skill_Tracking( WORD track_sel, bool bExec ) { ADDTOCALLSTACK("CClient::Cmd_Skill_Tracking"); // look around for stuff. ASSERT(m_pChar); if ( track_sel == USHRT_MAX ) { // Unlike others skills, Tracking is used during menu setup m_pChar->Skill_Cleanup(); // clean up current skill CMenuItem item[6]; item[0].m_sText = g_Cfg.GetDefaultMsg(DEFMSG_TRACKING_SKILLMENU_TITLE); item[1].m_id = ITEMID_TRACK_HORSE; item[1].m_color = 0; item[1].m_sText = g_Cfg.GetDefaultMsg(DEFMSG_TRACKING_SKILLMENU_ANIMALS); item[2].m_id = ITEMID_TRACK_OGRE; item[2].m_color = 0; item[2].m_sText = g_Cfg.GetDefaultMsg(DEFMSG_TRACKING_SKILLMENU_MONSTERS); item[3].m_id = ITEMID_TRACK_MAN; item[3].m_color = 0; item[3].m_sText = g_Cfg.GetDefaultMsg(DEFMSG_TRACKING_SKILLMENU_NPCS); item[4].m_id = ITEMID_TRACK_WOMAN; item[4].m_color = 0; item[4].m_sText = g_Cfg.GetDefaultMsg(DEFMSG_TRACKING_SKILLMENU_PLAYERS); m_tmMenu.m_Item[0] = 0; addItemMenu(CLIMODE_MENU_SKILL_TRACK_SETUP, item, 4); return true; } if ( track_sel > 0 ) // Not Cancelled { ASSERT(track_sel < COUNTOF(m_tmMenu.m_Item)); if ( bExec ) { // Tracking menu got us here. Start tracking the selected creature. m_pChar->SetTimeout(1 * TICK_PER_SEC); m_pChar->m_Act_Targ = static_cast<CGrayUID>(m_tmMenu.m_Item[track_sel]); m_pChar->Skill_Start(SKILL_TRACKING); return true; } static const NPCBRAIN_TYPE sm_Track_Brain[] = { NPCBRAIN_QTY, // not used here NPCBRAIN_ANIMAL, NPCBRAIN_MONSTER, NPCBRAIN_HUMAN, NPCBRAIN_NONE // players }; if ( track_sel >= COUNTOF(sm_Track_Brain) ) track_sel = COUNTOF(sm_Track_Brain) - 1; NPCBRAIN_TYPE track_type = sm_Track_Brain[track_sel]; CMenuItem item[minimum(MAX_MENU_ITEMS, COUNTOF(m_tmMenu.m_Item))]; size_t count = 0; item[0].m_sText = g_Cfg.GetDefaultMsg(DEFMSG_TRACKING_SKILLMENU_TITLE); m_tmMenu.m_Item[0] = track_sel; CWorldSearch AreaChars(m_pChar->GetTopPoint(), m_pChar->Skill_GetBase(SKILL_TRACKING) / 10 + 10); for (;;) { CChar *pChar = AreaChars.GetChar(); if ( !pChar ) break; if ( pChar == m_pChar ) continue; if ( pChar->GetNPCBrain() != track_type ) continue; if ( pChar->IsStatFlag(STATF_DEAD) ) // can't track ghosts continue; if ( pChar->m_pPlayer ) { // Prevent track hidden GMs if ( pChar->IsStatFlag(STATF_Insubstantial) && (pChar->GetPrivLevel() > GetPrivLevel()) ) continue; // Check action difficulty when trying to track players int tracking = m_pChar->Skill_GetBase(SKILL_TRACKING); int detectHidden = m_pChar->Skill_GetBase(SKILL_DETECTINGHIDDEN); if ( (g_Cfg.m_iRacialFlags & RACIALF_ELF_DIFFTRACK) && pChar->IsElf() ) tracking /= 2; // elves are more difficult to track (Difficult to Track racial trait) int hiding = pChar->Skill_GetBase(SKILL_HIDING); int stealth = pChar->Skill_GetBase(SKILL_STEALTH); int divisor = maximum(hiding + stealth, 1); int chance; if ( g_Cfg.m_iFeatureSE & FEATURE_SE_UPDATE ) chance = 50 * (tracking * 2 + detectHidden) / divisor; else chance = 50 * (tracking + detectHidden + 10 * Calc_GetRandVal(20)) / divisor; if ( Calc_GetRandVal(100) > chance ) continue; } CCharBase *pCharDef = pChar->Char_GetDef(); if ( !pCharDef ) continue; count++; item[count].m_id = static_cast<WORD>(pCharDef->m_trackID); item[count].m_color = 0; item[count].m_sText = pChar->GetName(); m_tmMenu.m_Item[count] = pChar->GetUID(); if ( count >= COUNTOF(item) - 1 ) break; } // Some credit for trying if ( count > 0 ) { m_pChar->Skill_UseQuick(SKILL_TRACKING, 20 + Calc_GetRandLLVal(30)); ASSERT(count < COUNTOF(item)); addItemMenu(CLIMODE_MENU_SKILL_TRACK, item, count); return true; } else { // Tracking failed or cancelled m_pChar->Skill_UseQuick(SKILL_TRACKING, 10 + Calc_GetRandLLVal(30)); static LPCTSTR const sm_Track_FailMsg[] = { g_Cfg.GetDefaultMsg(DEFMSG_TRACKING_FAIL_ANIMAL), g_Cfg.GetDefaultMsg(DEFMSG_TRACKING_FAIL_MONSTER), g_Cfg.GetDefaultMsg(DEFMSG_TRACKING_FAIL_PEOPLE), g_Cfg.GetDefaultMsg(DEFMSG_TRACKING_FAIL_PEOPLE) }; if ( sm_Track_FailMsg[track_sel - 1] ) SysMessage(sm_Track_FailMsg[track_sel - 1]); } } return false; }
void CSector::OnTick(int iPulseCount) { ADDTOCALLSTACK_INTENSIVE("CSector::OnTick"); // CWorld gives OnTick() to all CSectors. EXC_TRY("Tick"); EXC_SET("light change"); // do not tick sectors on maps not supported by server if ( !g_MapList.m_maps[m_map] ) return; // Check for light change before putting the sector to sleep, since in other case the // world light levels will be shitty bool fEnvironChange = false; bool fLightChange = false; bool fSleeping = false; if ( ! ( iPulseCount & 0x7f )) // 30 seconds or so. { // check for local light level change ? BYTE blightprv = m_Env.m_Light; m_Env.m_Light = GetLightCalc( false ); if ( m_Env.m_Light != blightprv ) { fEnvironChange = true; fLightChange = true; } } EXC_SET("sector sleeping?"); size_t clients = m_Chars_Active.HasClients(); if ( clients <= 0 ) // having no clients inside { // Put the sector to sleep if no clients been here in a while. fSleeping = IsSectorSleeping(); if ( fSleeping ) { if ( !g_Cfg.m_iSectorSleepMask ) return; if (( iPulseCount & g_Cfg.m_iSectorSleepMask ) != ( GetIndex() & g_Cfg.m_iSectorSleepMask )) return; } } EXC_SET("sound effects"); // random weather noises and effects. SOUND_TYPE sound = 0; bool fWeatherChange = false; int iRegionPeriodic = 0; if ( ! ( iPulseCount & 0x7f )) // 30 seconds or so. { // Only do this every x minutes or so (TICK_PER_SEC) // check for local weather change ? WEATHER_TYPE weatherprv = m_Env.m_Weather; if ( ! Calc_GetRandVal( 30 )) // change less often { m_Env.m_Weather = GetWeatherCalc(); if ( weatherprv != m_Env.m_Weather ) { fWeatherChange = true; fEnvironChange = true; } } // Random area noises. Only do if clients about. if ( clients > 0 ) { iRegionPeriodic = 2; static const SOUND_TYPE sm_SfxRain[] = { 0x10, 0x11 }; static const SOUND_TYPE sm_SfxWind[] = { 0x14, 0x15, 0x16 }; static const SOUND_TYPE sm_SfxThunder[] = { 0x28, 0x29 , 0x206 }; // Lightning ? // wind, rain, switch ( GetWeather() ) { case WEATHER_CLOUDY: break; case WEATHER_SNOW: if ( ! Calc_GetRandVal(5) ) sound = sm_SfxWind[ Calc_GetRandVal( COUNTOF( sm_SfxWind )) ]; break; case WEATHER_RAIN: { int iVal = Calc_GetRandVal(30); if ( iVal < 5 ) { // Mess up the light levels for a sec.. LightFlash(); sound = sm_SfxThunder[ Calc_GetRandVal( COUNTOF( sm_SfxThunder )) ]; } else if ( iVal < 10 ) sound = sm_SfxRain[ Calc_GetRandVal( COUNTOF( sm_SfxRain )) ]; else if ( iVal < 15 ) sound = sm_SfxWind[ Calc_GetRandVal( COUNTOF( sm_SfxWind )) ]; } break; default: break; } } } // regen all creatures and do AI ProfileTask charactersTask(PROFILE_CHARS); //pChar = STATIC_CAST <CChar*>( m_Chars_Active.GetHead()); CChar * pCharNext = NULL; CChar * pChar = dynamic_cast <CChar*>( m_Chars_Active.GetHead()); for ( ; pChar != NULL; pChar = pCharNext ) { EXC_TRYSUB("TickChar"); pCharNext = pChar->GetNext(); if (( fEnvironChange ) && ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) )) pChar->OnTrigger(CTRIG_EnvironChange, pChar); if ( pChar->IsClient()) { CClient * pClient = pChar->GetClient(); ASSERT( pClient ); if ( sound ) pClient->addSound(sound, pChar); if ( fLightChange && ! pChar->IsStatFlag( STATF_DEAD | STATF_NightSight )) pClient->addLight(); if ( fWeatherChange ) pClient->addWeather(GetWeather()); if ( iRegionPeriodic && pChar->m_pArea ) { if (( iRegionPeriodic == 2 )&&( IsTrigUsed(TRIGGER_REGPERIODIC) )) { pChar->m_pArea->OnRegionTrigger( pChar, RTRIG_REGPERIODIC ); iRegionPeriodic--; } if ( IsTrigUsed(TRIGGER_CLIPERIODIC) ) pChar->m_pArea->OnRegionTrigger( pChar, RTRIG_CLIPERIODIC ); } } // Can only die on your own tick. if ( !pChar->OnTick() ) pChar->Delete(); EXC_CATCHSUB("Sector"); EXC_DEBUGSUB_START; CPointMap pt = GetBasePoint(); g_Log.EventDebug("char 0%lx '%s'\n", static_cast<DWORD>(pChar->GetUID()), pChar->GetName()); g_Log.EventDebug("sector #%d [%d,%d,%d,%d]\n", GetIndex(), pt.m_x, pt.m_y, pt.m_z, pt.m_map); EXC_DEBUGSUB_END; } // decay items on ground = time out spells / gates etc.. etc.. // No need to check these so often ! ProfileTask itemsTask(PROFILE_ITEMS); CItem * pItemNext = NULL; CItem * pItem = dynamic_cast <CItem*>( m_Items_Timer.GetHead()); for ( ; pItem != NULL; pItem = pItemNext ) { EXC_TRYSUB("TickItem"); pItemNext = pItem->GetNext(); EXC_SETSUB("TimerExpired"); if ( pItem->IsTimerExpired() ) { EXC_SETSUB("ItemTick"); if ( !pItem->OnTick() ) { EXC_SETSUB("ItemDelete"); pItem->Delete(); } else { EXC_SETSUB("TimerExpired2"); if ( pItem->IsTimerExpired() ) // forgot to clear the timer.? strange. { EXC_SETSUB("SetTimeout"); pItem->SetTimeout(-1); } } } EXC_SETSUB("UpdateFlags"); pItem->OnTickStatusUpdate(); #ifdef _WIN32 EXC_CATCHSUB("Sector"); EXC_DEBUGSUB_START; CPointMap pt = GetBasePoint(); g_Log.EventError("item 0%lx '%s' [timer=%lld, type=%lld]\n", static_cast<DWORD>(pItem->GetUID()), pItem->GetName(), pItem->GetTimerAdjusted(), static_cast<int>(pItem->GetType())); g_Log.EventError("sector #%d [%d,%d,%d,%d]\n", GetIndex(), pt.m_x, pt.m_y, pt.m_z, pt.m_map); EXC_DEBUGSUB_END; #else } #ifndef _DEBUG catch ( const CGrayError& e )
bool CWebPageDef::r_Verb( CScript & s, CTextConsole * pSrc ) // some command on this object as a target { ADDTOCALLSTACK("CWebPageDef::r_Verb"); EXC_TRY("Verb"); ASSERT(pSrc); sm_iListIndex = 0; TCHAR *pszTmp2 = Str_GetTemp(); WV_TYPE iHeadKey = (WV_TYPE) FindTableSorted( s.GetKey(), sm_szVerbKeys, COUNTOF(sm_szVerbKeys)-1 ); switch ( iHeadKey ) { case WV_WEBPAGE: { // serv a web page to the pSrc CClient *pClient = dynamic_cast<CClient *>(pSrc); if ( !pClient ) return false; return ServPage(pClient, s.GetArgStr(), NULL); } case WV_CLIENTLIST: { ClientIterator it; for ( CClient *pClient = it.next(); pClient != NULL; pClient = it.next() ) { CChar *pChar = pClient->GetChar(); if ( !pChar ) continue; if ( pChar->IsStatFlag(STATF_Insubstantial) && (pClient->GetPrivLevel() > PLEVEL_Player) ) continue; sm_iListIndex++; LPCTSTR pszArgs = s.GetArgStr(); if ( pszArgs[0] == '\0' ) pszArgs = "<tr><td>%NAME%</td><td>%REGION.NAME%</td></tr>\n"; strcpy(pszTmp2, pszArgs); pChar->ParseText(Str_MakeFiltered(pszTmp2), &g_Serv, 1); pSrc->SysMessage(pszTmp2); } break; } case WV_GUILDLIST: case WV_TOWNLIST: { if ( !s.HasArgs() ) return false; IT_TYPE needtype = (iHeadKey == WV_GUILDLIST) ? IT_STONE_GUILD : IT_STONE_TOWN; for ( size_t i = 0; i < g_World.m_Stones.GetCount(); i++ ) { CItemStone *pStone = g_World.m_Stones[i]; if ( !pStone || !pStone->IsType(needtype) ) continue; sm_iListIndex++; strcpy(pszTmp2, s.GetArgStr()); pStone->ParseText(Str_MakeFiltered(pszTmp2), &g_Serv, 1); pSrc->SysMessage(pszTmp2); } break; } case WV_GMPAGELIST: { if ( !s.HasArgs() ) return false; CGMPage *pPage = static_cast<CGMPage *>(g_World.m_GMPages.GetHead()); for ( ; pPage != NULL; pPage = pPage->GetNext() ) { sm_iListIndex++; strcpy(pszTmp2, s.GetArgStr()); pPage->ParseText(Str_MakeFiltered(pszTmp2), &g_Serv, 1); pSrc->SysMessage(pszTmp2); } break; } default: return CResourceLink::r_Verb(s, pSrc); } return true; EXC_CATCH; EXC_DEBUG_START; EXC_ADD_SCRIPTSRC; EXC_DEBUG_END; return false; }
bool CSector::OnTick() { ADDTOCALLSTACK("CSector::OnTick"); /*Ticking sectors from CWorld * Timer is automatically updated at the end with a 30 seconds default delay * Any return before it will threat this CSector as Sleep and will make it * not tick again until a new player enters (WARNING: even if there are * players already inside). */ EXC_TRY("Tick"); // do not tick sectors on maps not supported by server if ( !g_MapList.m_maps[m_map] ) return true; EXC_SET_BLOCK("light change"); // Check for light change before putting the sector to sleep, since in other case the // world light levels will be shitty bool fEnvironChange = false; bool fLightChange = false; // check for local light level change ? byte bLightPrv = m_Env.m_Light; m_Env.m_Light = GetLightCalc( false ); if ( m_Env.m_Light != bLightPrv ) { fEnvironChange = true; fLightChange = true; } EXC_SET_BLOCK("sector sleeping?"); bool fCanSleep = CanSleep(true); int64 iCurTime = CServerTime::GetCurrentTime().GetTimeRaw(); // Put the sector to sleep if no clients been here in a while. if (fCanSleep && (g_Cfg._iSectorSleepDelay > 0)) { if (!IsSleeping()) { GoSleep(); } return true; } EXC_SET_BLOCK("sound effects"); // random weather noises and effects. SOUND_TYPE sound = 0; bool fWeatherChange = false; int iRegionPeriodic = 0; WEATHER_TYPE weatherprv = m_Env.m_Weather; if ( ! Calc_GetRandVal( 30 )) // change less often { m_Env.m_Weather = GetWeatherCalc(); if ( weatherprv != m_Env.m_Weather ) { fWeatherChange = true; fEnvironChange = true; } } // Random area noises. Only do if clients about. if ( GetClientsNumber() > 0 ) { iRegionPeriodic = 2; static const SOUND_TYPE sm_SfxRain[] = { 0x10, 0x11 }; static const SOUND_TYPE sm_SfxWind[] = { 0x14, 0x15, 0x16 }; static const SOUND_TYPE sm_SfxThunder[] = { 0x28, 0x29 , 0x206 }; // Lightning ? // wind, rain, switch ( GetWeather() ) { case WEATHER_CLOUDY: break; case WEATHER_SNOW: if ( ! Calc_GetRandVal(5) ) sound = sm_SfxWind[ Calc_GetRandVal( CountOf( sm_SfxWind )) ]; break; case WEATHER_RAIN: { int iVal = Calc_GetRandVal(30); if ( iVal < 5 ) { // Mess up the light levels for a sec.. LightFlash(); sound = sm_SfxThunder[ Calc_GetRandVal( CountOf( sm_SfxThunder )) ]; } else if ( iVal < 10 ) sound = sm_SfxRain[ Calc_GetRandVal( CountOf( sm_SfxRain )) ]; else if ( iVal < 15 ) sound = sm_SfxWind[ Calc_GetRandVal( CountOf( sm_SfxWind )) ]; } break; default: break; } } // Check environ changes and inform clients of it. ProfileTask charactersTask(PROFILE_CHARS); CChar * pCharNext = nullptr; CChar * pChar = static_cast <CChar*>( m_Chars_Active.GetHead()); for ( ; pChar != nullptr; pChar = pCharNext ) { EXC_TRYSUB("TickChar"); pCharNext = pChar->GetNext(); if (( fEnvironChange ) && ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) )) pChar->OnTrigger(CTRIG_EnvironChange, pChar); if ( pChar->IsClient()) { CClient * pClient = pChar->GetClient(); ASSERT( pClient ); if ( sound ) pClient->addSound(sound, pChar); if ( fLightChange && ! pChar->IsStatFlag( STATF_DEAD | STATF_NIGHTSIGHT )) pClient->addLight(); if ( fWeatherChange ) pClient->addWeather(GetWeather()); if ( iRegionPeriodic && pChar->m_pArea ) { if ( ( iRegionPeriodic == 2 ) && IsTrigUsed(TRIGGER_REGPERIODIC)) { pChar->m_pArea->OnRegionTrigger( pChar, RTRIG_REGPERIODIC ); --iRegionPeriodic; } if ( IsTrigUsed(TRIGGER_CLIPERIODIC) ) pChar->m_pArea->OnRegionTrigger( pChar, RTRIG_CLIPERIODIC ); } } EXC_CATCHSUB("Sector"); EXC_DEBUGSUB_START; CPointMap pt = GetBasePoint(); g_Log.EventDebug("#0 char 0%x '%s'\n", (dword)(pChar->GetUID()), pChar->GetName()); g_Log.EventDebug("#0 sector #%d [%d,%d,%d,%d]\n", GetIndex(), pt.m_x, pt.m_y, pt.m_z, pt.m_map); EXC_DEBUGSUB_END; } ProfileTask overheadTask(PROFILE_OVERHEAD); EXC_SET_BLOCK("check map cache"); if (fCanSleep && m_iMapBlockCacheTime < iCurTime) // Only if the sector can sleep. { // delete the static CServerMapBlock items that have not been used recently. m_iMapBlockCacheTime = CServerTime::GetCurrentTime().GetTimeRaw() + g_Cfg.m_iMapCacheTime ; CheckMapBlockCache(); } EXC_CATCH; SetTimeoutS(30); // Sector is Awake, make it tick after 30 seconds. EXC_DEBUG_START; CPointMap pt = GetBasePoint(); g_Log.EventError("#4 sector #%d [%hd,%hd,%hhd,%hhu]\n", GetIndex(), pt.m_x, pt.m_y, pt.m_z, pt.m_map); EXC_DEBUG_END; return true; }