Example #1
0
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 );
		}
	}
}
Example #2
0
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 );
}
Example #3
0
// ---------------------------------------------------------
bool CPartyDef::MessageEvent( CGrayUID uidDst, CGrayUID uidSrc, const NCHAR * pText, int ilenmsg )
{
	ADDTOCALLSTACK("CPartyDef::MessageEvent");
	UNREFERENCED_PARAMETER(ilenmsg);
	if ( pText == NULL )
		return( false );

	if ( uidDst && !IsInParty( uidDst.CharFind() ) )
		return( false );

	CChar * pFrom = uidSrc.CharFind();
	CChar * pTo = NULL;
	if ( uidDst != (DWORD) 0 )
		pTo = uidDst.CharFind();

	TCHAR * szText = Str_GetTemp();
	CvtNUNICODEToSystem( szText, MAX_TALK_BUFFER, pText, MAX_TALK_BUFFER );

	if ( ! m_pSpeechFunction.IsEmpty() )
	{
		TRIGRET_TYPE tr = TRIGRET_RET_FALSE;
		CScriptTriggerArgs Args;
		Args.m_iN1 = uidSrc;
		Args.m_iN2 = uidDst;
		Args.m_s1 = szText;
		Args.m_s1_raw = szText;

		if ( r_Call(m_pSpeechFunction, &g_Serv, &Args, NULL, &tr) )
		{
			if ( tr == TRIGRET_RET_TRUE )
				return( false );
		}
	}

	if ( g_Log.IsLoggedMask( LOGM_PLAYER_SPEAK ))
		g_Log.Event( LOGM_PLAYER_SPEAK, "%lx:'%s' Says '%s' in party to '%s'\n", pFrom->GetClient()->GetSocketID(), pFrom->GetName(), szText, pTo ? pTo->GetName() : "all" );

	sprintf(szText, g_Cfg.GetDefaultMsg( DEFMSG_PARTY_MSG ), pText);
	PacketPartyChat cmd(pFrom, pText);

	if ( pTo != NULL )
		SendMemberMsg(pTo, &cmd);
	else
		SendAll(&cmd);

	return( true );
}
Example #4
0
// ---------------------------------------------------------
void CPartyDef::AddStatsUpdate( CChar * pChar, PacketSend * pPacket )
{
	ADDTOCALLSTACK("CPartyDef::AddStatsUpdate");
	size_t iQty = m_Chars.GetCharCount();
	if ( iQty <= 0 )
		return;

	for ( size_t i = 0; i < iQty; i++ )
	{
		CChar * pCharNow = m_Chars.GetChar(i).CharFind();
		if ( pCharNow && pCharNow != pChar )
		{
			if ( pCharNow->CanSee( pChar ) && pCharNow->IsClient() )
				pPacket->send(pCharNow->GetClient());
		}
	}
}
Example #5
0
void CItemContainer::Trade_Delete()
{
	ADDTOCALLSTACK("CItemContainer::Trade_Delete");
	// Called when object deleted.

	ASSERT( IsType(IT_EQ_TRADE_WINDOW) );
	

	CChar * pChar = dynamic_cast <CChar*> (GetParent());
	if ( pChar == NULL )
		return;

	if ( pChar->IsClient())
	{
		// Send the cancel trade message.
		PacketTradeAction cmd(SECURE_TRADE_CLOSE);
		cmd.prepareClose(this);
		cmd.send(pChar->GetClient());
	}
	
	// Drop items back in my pack.
	CItem * pItemNext;
	for ( CItem* pItem = GetContentHead(); pItem!=NULL; pItem=pItemNext)
	{
		pItemNext = pItem->GetNext();
		pChar->ItemBounce( pItem );
	}
	
	// Kill my trading partner.
	CItemContainer * pPartner = dynamic_cast <CItemContainer *> ( m_uidLink.ItemFind());
	if ( pPartner == NULL )
		return;

	if ( IsTrigUsed(TRIGGER_TRADECLOSE) )
	{
		CChar * pChar2 = dynamic_cast <CChar*> (pPartner->GetParent());
		CScriptTriggerArgs Args( pChar2 );
		pChar->OnTrigger( CTRIG_TradeClose,  pChar , &Args );
		CScriptTriggerArgs Args2( pChar );
		pChar2->OnTrigger( CTRIG_TradeClose, pChar, &Args2);
	}

	m_uidLink.InitUID();	// unlink.
	pPartner->m_uidLink.InitUID();
	pPartner->Delete();
}
Example #6
0
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);
	}
}
Example #7
0
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 );
	}
}
Example #8
0
bool CCharPlayer::r_LoadVal( CChar * pChar, CScript &s )
{
	ADDTOCALLSTACK("CCharPlayer::r_LoadVal");
	EXC_TRY("LoadVal");
	
	LPCTSTR pszKey = s.GetKey();

	if ( !strnicmp(pszKey, "GMPAGE", 6) )		//	GM pages
	{
		pszKey += 6;
		if ( *pszKey == '.' )						//	GMPAGE.*
		{
			SKIP_SEPARATORS(pszKey);
			size_t index = Exp_GetVal(pszKey);
			if ( index >= g_World.m_GMPages.GetCount() )
				return false;

			CGMPage* pPage = STATIC_CAST <CGMPage*> (g_World.m_GMPages.GetAt(index));
			if ( pPage == NULL )
				return false;

			SKIP_SEPARATORS(pszKey);
			if ( !strcmpi(pszKey, "HANDLE") )
			{
				CChar *ppChar = pChar;
				LPCTSTR pszArgs = s.GetArgStr(); //Moved here because of error with quoted strings!?!?
				if ( *pszArgs )
					ppChar = dynamic_cast<CChar*>(g_World.FindUID(s.GetArgVal()));

				if ( ppChar == NULL )
					return false;

				CClient *pClient = ppChar->GetClient();
				if ( pClient == NULL )
					return false;

				pPage->SetGMHandler(pClient);
			}
			else if ( !strcmpi(pszKey, "DELETE") )
			{
				delete pPage;
			}
			else if ( pPage->FindGMHandler() )
			{
				CClient* pClient = pChar->GetClient();
				if ( pClient != NULL && pClient->GetChar() != NULL )
					pClient->Cmd_GM_PageCmd(pszKey);
			}
			else
			{
				return false;
			}

			return true;
		}
		return false;
	}
	else if ( ( !strnicmp(pszKey, "GUILD", 5) ) || ( !strnicmp(pszKey, "TOWN", 4) ) )
	{
		bool bIsGuild = !strnicmp(pszKey, "GUILD", 5);
		pszKey += bIsGuild ? 5 : 4;
		if ( *pszKey == '.' )
		{
			pszKey += 1;
			CItemStone *pMyGuild = pChar->Guild_Find(bIsGuild ? MEMORY_GUILD : MEMORY_TOWN);
			if ( pMyGuild ) return pMyGuild->r_SetVal(pszKey, s.GetArgRaw());
		}
		return false;
	}

	switch ( FindTableHeadSorted( s.GetKey(), sm_szLoadKeys, COUNTOF( sm_szLoadKeys )-1 ))
	{
		case CPC_DEATHS:
			m_wDeaths = static_cast<WORD>(s.GetArgVal());
			return true;
		case CPC_DSPEECH:
			return( m_Speech.r_LoadVal( s, RES_SPEECH ));
		case CPC_KILLS:
			m_wMurders = static_cast<WORD>(s.GetArgVal());
			pChar->NotoSave_Update();
			return true;
		case CPC_KRTOOLBARSTATUS:
			m_bKrToolbarEnabled = ( s.GetArgVal() != 0 );
			if ( pChar->IsClient() )
				pChar->GetClient()->addKRToolbar( m_bKrToolbarEnabled );
			return true;
		case CPC_LASTUSED:
			m_timeLastUsed = CServTime::GetCurrentTime() - ( s.GetArgVal() * TICK_PER_SEC );
			return( true );
		case CPC_PFLAG:
			{
				m_pflag = s.GetArgVal();
			} return( true );
		case CPC_PROFILE:
			m_sProfile = Str_MakeFiltered( s.GetArgStr());
			return( true );
		case CPC_REFUSETRADES:
			pChar->SetDefNum(s.GetKey(), s.GetArgVal() > 0 ? 1 : 0, false);
			return( true );
		case CPC_SKILLCLASS:
			return SetSkillClass( pChar, g_Cfg.ResourceGetIDType( RES_SKILLCLASS, s.GetArgStr()));
		case CPC_SKILLLOCK:
			{
				SKILL_TYPE skill = Skill_GetLockType( s.GetKey());
				if ( skill <= SKILL_NONE )
					return false;
				int bState = s.GetArgVal();
				if ( bState < SKILLLOCK_UP || bState > SKILLLOCK_LOCK )
					return false;
				Skill_SetLock(skill, static_cast<SKILLLOCK_TYPE>(bState));
			} return true;
		case CPC_SPEEDMODE:
			{
				m_speedMode = static_cast<unsigned short>(s.GetArgVal());
				pChar->UpdateSpeedMode();
			} return true;
		case CPC_STATLOCK:
			{
				STAT_TYPE stat = Stat_GetLockType( s.GetKey());
				if (( stat <= STAT_NONE ) || ( stat >= STAT_BASE_QTY ))
					return false;
				int bState = s.GetArgVal();
				if ( bState < SKILLLOCK_UP || bState > SKILLLOCK_LOCK )
					return false;
				Stat_SetLock(stat, static_cast<SKILLLOCK_TYPE>(bState));
			} return true;

		default:
			// Just ignore any NPC type stuff.
			if ( FindTableSorted( s.GetKey(), CCharNPC::sm_szLoadKeys, COUNTOF( CCharNPC::sm_szLoadKeys )-1 ) >= 0 )
			{
				return( true );
			}
			return( false );
	}
	EXC_CATCH;

	EXC_DEBUG_START;
	EXC_ADD_SCRIPT;
	EXC_DEBUG_END;
	return false;
}
Example #9
0
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 )
Example #10
0
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;
}