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 != nullptr; 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". ushort uiDist = pChar->m_pNPC->m_Home_Dist_Wander; pChar->MoveNear( pChar->m_ptHome, uiDist ); pChar->NPC_CreateTrigger(); //Removed from NPC_LoadScript() and triggered after char placement pChar->Spell_Resurrection(); } }
void CItem::Spawn_KillChildren() { // kill all creatures spawned from this ! DEBUG_CHECK( IsType(IT_SPAWN_CHAR)); int iCurrent = m_itSpawnChar.m_current; for ( int j = 0; j < 256; j++ ) // loop through all maps { if ( !g_MapList.m_maps[j] ) continue; // skip unsupported maps for ( int i = 0; i < g_MapList.GetSectorQty(j); i++ ) { CSector * pSector = g_World.GetSector(j, i); ASSERT(pSector); CChar * pCharNext; CChar * pChar = STATIC_CAST <CChar*>( pSector->m_Chars_Active.GetHead()); for ( ; pChar!=NULL; pChar = pCharNext ) { pCharNext = pChar->GetNext(); if ( pChar->NPC_IsSpawnedBy( this )) { pChar->Delete(); iCurrent --; } } } } if (iCurrent && ! g_Serv.IsLoading()) { DEBUG_CHECK(iCurrent==0); } m_itSpawnChar.m_current = 0; // Should not be necessary Spawn_OnTick( false ); }
void CSector::Restock() { ADDTOCALLSTACK("CSector::Restock"); // ARGS: iTime = time in seconds // set restock time of all vendors in Sector. // set the respawn time of all spawns in Sector. CChar * pCharNext; CChar * pChar = dynamic_cast <CChar*>( m_Chars_Active.GetHead()); for ( ; pChar; pChar = pCharNext ) { pCharNext = pChar->GetNext(); pChar->NPC_Vendor_Restock(true); } CItem * pItemNext; CItem * pItem = dynamic_cast <CItem*>( m_Items_Timer.GetHead()); for ( ; pItem; pItem = pItemNext ) { pItemNext = pItem->GetNext(); if (pItem->IsType(IT_SPAWN_ITEM) || pItem->IsType(IT_SPAWN_CHAR)) { static_cast<CItemSpawn*>(pItem)->OnTick(true); } } }
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->m_pClient ) { if ( fFlash ) // This does not seem to work predicably ! { BYTE bPrvLight = m_Env.m_Light; m_Env.m_Light = LIGHT_BRIGHT; // full bright. pChar->m_pClient->addLight(); m_Env.m_Light = bPrvLight; // back to previous. } pChar->m_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 ); } } }
void CSector::SetSeason( SEASON_TYPE season ) { ADDTOCALLSTACK("CSector::SetSeason"); // Set the season type. if ( season == m_Env.m_Season ) return; m_Env.m_Season = season; CChar * pChar = STATIC_CAST <CChar*>( m_Chars_Active.GetHead()); for ( ; pChar != NULL; pChar = pChar->GetNext()) { if ( pChar->IsClient() ) pChar->GetClient()->addSeason(season); if ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) ) pChar->OnTrigger(CTRIG_EnvironChange, pChar); } }
void CSector::SetWeather( WEATHER_TYPE w ) { ADDTOCALLSTACK("CSector::SetWeather"); // Set the immediate weather type. // 0=dry, 1=rain or 2=snow. if ( w == m_Env.m_Weather ) return; m_Env.m_Weather = w; CChar * pChar = STATIC_CAST <CChar*>( m_Chars_Active.GetHead()); for ( ; pChar != NULL; pChar = pChar->GetNext()) { if ( pChar->IsClient()) pChar->GetClient()->addWeather( w ); if ( IsTrigUsed(TRIGGER_ENVIRONCHANGE) ) pChar->OnTrigger( CTRIG_EnvironChange, pChar ); } }
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 )
void CSector::r_Write() { ADDTOCALLSTACK_INTENSIVE("CSector::r_Write"); if ( m_fSaveParity == g_World.m_fSaveParity ) return; // already saved. CPointMap pt = GetBasePoint(); m_fSaveParity = g_World.m_fSaveParity; bool bHeaderCreated = false; if ( m_dwFlags > 0) { g_World.m_FileWorld.WriteSection("SECTOR %d,%d,0,%d", pt.m_x, pt.m_y, pt.m_map ); g_World.m_FileWorld.WriteKeyHex("FLAGS", m_dwFlags); bHeaderCreated = true; } if (g_Cfg.m_bAllowLightOverride && IsLightOverriden()) { if ( bHeaderCreated == false ) { g_World.m_FileWorld.WriteSection("SECTOR %d,%d,0,%d", pt.m_x, pt.m_y, pt.m_map); bHeaderCreated = true; } g_World.m_FileWorld.WriteKeyVal("LIGHT", GetLight()); } if (!g_Cfg.m_fNoWeather && (IsRainOverriden() || IsColdOverriden())) { if ( bHeaderCreated == false ) { g_World.m_FileWorld.WriteSection("SECTOR %d,%d,0,%d", pt.m_x, pt.m_y, pt.m_map); bHeaderCreated = true; } if ( IsRainOverriden() ) g_World.m_FileWorld.WriteKeyVal("RAINCHANCE", GetRainChance()); if ( IsColdOverriden() ) g_World.m_FileWorld.WriteKeyVal("COLDCHANCE", GetColdChance()); } if (GetSeason() != SEASON_Summer) { if ( bHeaderCreated == false ) g_World.m_FileWorld.WriteSection("SECTOR %d,%d,0,%d", pt.m_x, pt.m_y, pt.m_map); g_World.m_FileWorld.WriteKeyVal("SEASON", GetSeason()); } // Chars in the sector. CChar *pCharNext = NULL; for ( CChar *pChar = static_cast<CChar*>(m_Chars_Active.GetHead()); pChar != NULL; pChar = pCharNext ) { pCharNext = pChar->GetNext(); pChar->r_WriteParity(pChar->m_pPlayer ? g_World.m_FilePlayers : g_World.m_FileWorld); } // Inactive Client Chars, ridden horses and dead NPCs (NOTE: Push inactive player chars out to the account files here?) for ( CChar *pChar = static_cast<CChar*>(m_Chars_Disconnect.GetHead()); pChar != NULL; pChar = pCharNext ) { pCharNext = pChar->GetNext(); pChar->r_WriteParity(pChar->m_pPlayer ? g_World.m_FilePlayers : g_World.m_FileWorld); } // Items on the ground. CItem *pItemNext = NULL; for ( CItem *pItem = static_cast<CItem*>(m_Items_Inert.GetHead()); pItem != NULL; pItem = pItemNext ) { pItemNext = pItem->GetNext(); if ( pItem->IsType(IT_MULTI_CUSTOM) ) pItem->r_WriteSafe(g_World.m_FileMultis); else if ( !pItem->IsAttr(ATTR_STATIC) ) pItem->r_WriteSafe(g_World.m_FileWorld); } for ( CItem *pItem = static_cast<CItem*>(m_Items_Timer.GetHead()); pItem != NULL; pItem = pItemNext ) { pItemNext = pItem->GetNext(); if ( pItem->IsType(IT_MULTI_CUSTOM) ) pItem->r_WriteSafe(g_World.m_FileMultis); else if ( !pItem->IsAttr(ATTR_STATIC) ) pItem->r_WriteSafe(g_World.m_FileWorld); } }
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; }