void CSGameState::AllHostagesGone()
{
	for (int i = 0; i < m_hostageCount; i++)
		m_hostage[i].isValid = false;
}

/*
*
*   respects for all of the code used other than the HL Engine and MODs
*   from Valve.  If you modify this file, you may extend this exception
*   to your version of the file, but you are not obligated to do so.  If
*   you do not wish to do so, delete this exception statement from your
*   version.
*
*/

#include "precompiled.h"

CSGameState::CSGameState(CCSBot *owner)
{
	m_owner = owner;
	m_isRoundOver = false;
	m_bombState = MOVING;

	m_lastSawBomber.Invalidate();
	m_lastSawLooseBomb.Invalidate();
	m_validateInterval.Invalidate();
	m_isPlantedBombPosKnown = false;
	m_plantedBombsite = UNKNOWN;

	m_bombsiteCount = 0;
	m_bombsiteSearchIndex = 0;

	for (int i = 0; i < MAX_HOSTAGES; i++)
	{
		HostageInfo *info = &m_hostage[i];

		info->hostage = nullptr;
		info->knownPos = Vector(0, 0, 0);
		info->isValid = false;
		info->isAlive = false;
		info->isFree = true;
	}
}

// Reset at round start
void CSGameState::Reset()
{
	int i;
	m_isRoundOver = false;

	// bomb
	m_bombState = MOVING;
	m_lastSawBomber.Invalidate();
	m_lastSawLooseBomb.Invalidate();
	m_bombsiteCount = TheCSBots()->GetZoneCount();

	m_isPlantedBombPosKnown = false;
	m_plantedBombsite = UNKNOWN;

	for (i = 0; i < m_bombsiteCount; i++)
	{
		m_isBombsiteClear[i] = false;
		m_bombsiteSearchOrder[i] = i;
	}

	// shuffle the bombsite search order
	// allows T's to plant at random site, and TEAM_CT's to search in a random order
	// NOTE: VS6 std::random_shuffle() doesn't work well with an array of two elements (most maps)
	for (i = 0; i < m_bombsiteCount; i++)
	{
		int swap = m_bombsiteSearchOrder[i];
		int rnd = RANDOM_LONG(i, m_bombsiteCount - 1);

		m_bombsiteSearchOrder[i] = m_bombsiteSearchOrder[rnd];
		m_bombsiteSearchOrder[rnd] = swap;
	}

	m_bombsiteSearchIndex = 0;
	InitializeHostageInfo();
}

// Update game state based on events we have received
void CSGameState::OnEvent(GameEventType event, CBaseEntity *pEntity, CBaseEntity *pOther)
{
	switch (event)
	{
	case EVENT_BOMB_PLANTED:
	{
		// change state - the event is announced to everyone
		SetBombState(PLANTED);

		// Terrorists always know where the bomb is
		if (m_owner->m_iTeam == TERRORIST && pOther)
		{
			UpdatePlantedBomb(&pOther->pev->origin);
		}
		break;
	}
	case EVENT_BOMB_DEFUSED:
		SetBombState(DEFUSED);
		break;
	case EVENT_BOMB_EXPLODED:
		SetBombState(EXPLODED);
		break;
	case EVENT_ALL_HOSTAGES_RESCUED:
		m_allHostagesRescued = true;
		break;
	case EVENT_TERRORISTS_WIN:
	case EVENT_CTS_WIN:
	case EVENT_ROUND_DRAW:
		m_isRoundOver = true;
		break;
	default:
		break;
	}
}

// True if round has been won or lost (but not yet reset)
bool CSGameState::IsRoundOver() const
{
	return m_isRoundOver;
}

void CSGameState::SetBombState(BombState state)
{
	// if state changed, reset "last seen" timestamps
	if (m_bombState != state)
	{
		m_bombState = state;
	}
}

void CSGameState::UpdateLooseBomb(const Vector *pos)
{
	m_looseBombPos = *pos;
	m_lastSawLooseBomb.Reset();

	// we saw the loose bomb, update our state
	SetBombState(LOOSE);
}

float CSGameState::TimeSinceLastSawLooseBomb() const
{
	return m_lastSawLooseBomb.GetElapsedTime();
}

bool CSGameState::IsLooseBombLocationKnown() const
{
	if (m_bombState != LOOSE)
		return false;

	return (m_lastSawLooseBomb.HasStarted()) ? true : false;
}

void CSGameState::UpdateBomber(const Vector *pos)
{
	m_bomberPos = *pos;
	m_lastSawBomber.Reset();

	// we saw the bomber, update our state
	SetBombState(MOVING);
}

float CSGameState::TimeSinceLastSawBomber() const
{
	return m_lastSawBomber.GetElapsedTime();
}

bool CSGameState::IsPlantedBombLocationKnown() const
{
	if (m_bombState != PLANTED)
		return false;

	return m_isPlantedBombPosKnown;
}

// Return the zone index of the planted bombsite, or UNKNOWN
int CSGameState::GetPlantedBombsite() const
{
	if (m_bombState != PLANTED)
		return UNKNOWN;

	return m_plantedBombsite;
}

// Return true if we are currently in the bombsite where the bomb is planted
bool CSGameState::IsAtPlantedBombsite() const
{
	if (m_bombState != PLANTED)
		return false;

	const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone(&m_owner->pev->origin);

	if (zone)
	{
		return (m_plantedBombsite == zone->m_index);
	}

	return false;
}

// Return the zone index of the next bombsite to search
int CSGameState::GetNextBombsiteToSearch()
{
	if (m_bombsiteCount <= 0)
		return 0;

	int i;
	// return next non-cleared bombsite index
	for (i = m_bombsiteSearchIndex; i < m_bombsiteCount; i++)
	{
		int z = m_bombsiteSearchOrder[i];
		if (!m_isBombsiteClear[z])
		{
			m_bombsiteSearchIndex = i;
			return z;
		}
	}

	// all the bombsites are clear, someone must have been mistaken - start search over
	for (i = 0; i < m_bombsiteCount; i++)
	{
		m_isBombsiteClear[i] = false;
	}

	m_bombsiteSearchIndex = 0;
	return GetNextBombsiteToSearch();
}

// Returns position of bomb in its various states (moving, loose, planted),
// or NULL if we don't know where the bomb is
const Vector *CSGameState::GetBombPosition() const
{
	switch (m_bombState)
	{
	case MOVING:
	{
		if (!m_lastSawBomber.HasStarted())
			return nullptr;

		return &m_bomberPos;
	}
	case LOOSE:
	{
		if (IsLooseBombLocationKnown())
			return &m_looseBombPos;

		return nullptr;
	}
	case PLANTED:
	{
		if (IsPlantedBombLocationKnown())
			return &m_plantedBombPos;

		return nullptr;
	}
	}

	return nullptr;
}

// We see the planted bomb at 'pos'
void CSGameState::UpdatePlantedBomb(const Vector *pos)
{
	const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone(pos);

	if (!zone)
	{
		CONSOLE_ECHO("ERROR: Bomb planted outside of a zone!\n");
		m_plantedBombsite = UNKNOWN;
	}
	else
	{
		m_plantedBombsite = zone->m_index;
	}

	m_plantedBombPos = *pos;
	m_isPlantedBombPosKnown = true;
	SetBombState(PLANTED);
}

// Someone told us where the bomb is planted
void CSGameState::MarkBombsiteAsPlanted(int zoneIndex)
{
	m_plantedBombsite = zoneIndex;
	SetBombState(PLANTED);
}

// Someone told us a bombsite is clear
void CSGameState::ClearBombsite(int zoneIndex)
{
	if (zoneIndex >= 0 && zoneIndex < m_bombsiteCount)
		m_isBombsiteClear[zoneIndex] = true;
}

bool CSGameState::IsBombsiteClear(int zoneIndex) const
{
	if (zoneIndex >= 0 && zoneIndex < m_bombsiteCount)
		return m_isBombsiteClear[zoneIndex];

	return false;
}

void CSGameState::InitializeHostageInfo()
{
	m_hostageCount = 0;
	m_allHostagesRescued = false;
	m_haveSomeHostagesBeenTaken = false;

	CHostage *pHostage = nullptr;
	while ((pHostage = UTIL_FindEntityByClassname(pHostage, "hostage_entity")))
	{
		if (m_hostageCount >= MAX_HOSTAGES)
			break;

		if (!pHostage->IsAlive())
			continue;

		m_hostage[m_hostageCount].hostage = pHostage;
		m_hostage[m_hostageCount].knownPos = pHostage->pev->origin;
		m_hostage[m_hostageCount].isValid = true;
		m_hostage[m_hostageCount].isAlive = true;
		m_hostage[m_hostageCount].isFree = true;
		m_hostageCount++;
	}
}

// Return the closest free and live hostage
// If we are a CT this information is perfect.
// Otherwise, this is based on our individual memory of the game state.
// If NULL is returned, we don't think there are any hostages left, or we dont know where they are.
// NOTE: a T can remember a hostage who has died.  knowPos will be filled in, but NULL will be
// returned, since CHostages get deleted when they die.
CHostage *CSGameState::GetNearestFreeHostage(Vector *knowPos) const
{
	if (!m_owner)
		return nullptr;

	CNavArea *startArea = m_owner->GetLastKnownArea();
	if (!startArea)
		return nullptr;

	CHostage *close = nullptr;
	const Vector *closePos = nullptr;
	float closeDistance = 9999999999.9f;

	for (int i = 0; i < m_hostageCount; i++)
	{
		CHostage *pHostage = m_hostage[i].hostage;
		const Vector *hostagePos = nullptr;

		if (m_owner->m_iTeam == CT)
		{
			// we know exactly where the hostages are, and if they are alive
			if (!m_hostage[i].hostage || !m_hostage[i].hostage->IsAlive())
				continue;

			if (m_hostage[i].hostage->IsFollowingSomeone())
				continue;

			hostagePos = &pHostage->pev->origin;
		}
		else
		{
			// use our memory of where we think the hostages are
			if (m_hostage[i].isValid == false)
				continue;

			hostagePos = &m_hostage[i].knownPos;
		}

		CNavArea *hostageArea = TheNavAreaGrid.GetNearestNavArea(hostagePos);
		if (hostageArea)
		{
			ShortestPathCost pc;
			float travelDistance = NavAreaTravelDistance(startArea, hostageArea, pc);

			if (travelDistance >= 0.0f && travelDistance < closeDistance)
			{
				closePos = hostagePos;
				closeDistance = travelDistance;
				close = pHostage;
			}
		}
	}

	// return where we think the hostage is
	if (knowPos && closePos)
	{
		knowPos = const_cast<Vector *>(closePos);
	}

	return close;
}

// Return the location of a "free" hostage, or NULL if we dont know of any
const Vector *CSGameState::GetRandomFreeHostagePosition()
{
	const Vector *freePos[MAX_HOSTAGES];
	int freeCount = 0;

	if (!m_owner)
		return nullptr;

	for (int i = 0; i < m_hostageCount; i++)
	{
		const HostageInfo *info = &m_hostage[i];
		const Vector *hostagePos = nullptr;

		if (m_owner->m_iTeam == CT)
		{
			// we know exactly where the hostages are, and if they are alive
			if (!info->hostage || !info->hostage->IsAlive())
				continue;

			// escorted hostages are not "free"
			if (info->hostage->IsFollowingSomeone())
				continue;

			freePos[freeCount++] = &info->hostage->pev->origin;
		}
		else
		{
			// use our memory of where we think the hostages are
			if (info->isValid == false)
				continue;

			freePos[freeCount++] = &info->knownPos;
		}
	}

	if (freeCount)
	{
		return freePos[RANDOM_LONG(0, freeCount - 1)];
	}

	return nullptr;
}

// If we can see any of the positions where we think a hostage is, validate it
// Return status of any changes (a hostage died or was moved)
CSGameState::ValidateStatusType CSGameState::ValidateHostagePositions()
{
	// limit how often we validate
	if (!m_validateInterval.IsElapsed())
		return NO_CHANGE;

	const float validateInterval = 0.5f;
	m_validateInterval.Start(validateInterval);

	// check the status of hostages
	unsigned char status = NO_CHANGE;

	int i;
	int startValidCount = 0;
	for (i = 0; i < m_hostageCount; i++)
	{
		if (m_hostage[i].isValid)
			startValidCount++;
	}

	for (i = 0; i < m_hostageCount; i++)
	{
		HostageInfo *info = &m_hostage[i];

		if (!info->hostage)
			continue;

		// if we can see a hostage, update our knowledge of it
		if (m_owner->IsVisible(&info->hostage->pev->origin, CHECK_FOV))
		{
			if (info->hostage->IsAlive())
			{
				// live hostage
				// if hostage is being escorted by a CT, we don't "see" it, we see the CT
				if (info->hostage->IsFollowingSomeone())
				{
					info->isValid = false;
				}
				else
				{
					info->knownPos = info->hostage->pev->origin;
					info->isValid = true;
				}
			}
			else
			{
				// dead hostage
				// if we thought it was alive, this is news to us
				if (info->isAlive)
					status |= HOSTAGE_DIED;

				info->isAlive = false;
				info->isValid = false;
			}

			continue;
		}

		// if we dont know where this hostage is, nothing to validate
		if (!info->isValid)
			continue;

		// can't directly see this hostage
		// check line of sight to where we think this hostage is, to see if we noticed that is has moved
		if (m_owner->IsVisible(&info->knownPos, CHECK_FOV))
		{
			// we can see where we thought the hostage was - verify it is still there and alive
			if (!info->hostage->IsAlive())
			{
				// since we have line of sight to an invalid hostage, it must be dead
				// discovered that hostage has been killed
				status |= HOSTAGE_DIED;

				info->isAlive = false;
				info->isValid = false;
				continue;
			}

			if (info->hostage->IsFollowingSomeone())
			{
				// discovered the hostage has been taken
				status |= HOSTAGE_GONE;
				info->isValid = false;
				continue;
			}

			const float tolerance = 50.0f;
			if ((info->hostage->pev->origin - info->knownPos).IsLengthGreaterThan(tolerance))
			{
				// discovered that hostage has been moved
				status |= HOSTAGE_GONE;
				info->isValid = false;
				continue;
			}
		}
	}

	int endValidCount = 0;
	for (i = 0; i < m_hostageCount; i++)
	{
		if (m_hostage[i].isValid)
			endValidCount++;
	}

	if (endValidCount == 0 && startValidCount > 0)
	{
		// we discovered all the hostages are gone
		status &= ~HOSTAGE_GONE;
		status |= HOSTAGES_ALL_GONE;
	}

	return static_cast<ValidateStatusType>(status);
}

// Return the nearest visible free hostage
// Since we can actually see any hostage we return, we know its actual position
CHostage *CSGameState::GetNearestVisibleFreeHostage() const
{
	CHostage *close = nullptr;
	float closeRangeSq = 999999999.9f;
	float rangeSq;

	Vector pos;

	for (int i = 0; i < m_hostageCount; i++)
	{
		const HostageInfo *info = &m_hostage[i];

		if (!info->hostage)
			continue;

		// if the hostage is dead or rescued, its not free
		if (!info->hostage->IsAlive())
			continue;

		// if this hostage is following someone, its not free
		if (info->hostage->IsFollowingSomeone())
			continue;

		// TODO: Use travel distance here
		pos = info->hostage->pev->origin + Vector(0, 0, HumanHeight * 0.75f);
		rangeSq = (pos - m_owner->pev->origin).LengthSquared();

		if (rangeSq < closeRangeSq)
		{
			if (!m_owner->IsVisible(&pos))
				continue;

			close = info->hostage;
			closeRangeSq = rangeSq;
		}
	}

	return close;
}

// Return true if there are no free hostages
bool CSGameState::AreAllHostagesBeingRescued() const
{
	// if the hostages have all been rescued, they are not being rescued any longer
	if (m_allHostagesRescued)
		return false;

	bool isAllDead = true;
	for (int i = 0; i < m_hostageCount; i++)
	{
		const HostageInfo *info = &m_hostage[i];

		if (m_owner->m_iTeam == CT)
		{
			// CT's have perfect knowledge via their radar
			if (info->hostage && info->hostage->IsAlive())
			{
				if (!info->hostage->IsFollowingSomeone())
					return false;

				isAllDead = false;
			}
		}
		else
		{
			if (info->isValid && info->isAlive)
				return false;

			if (info->isAlive)
				isAllDead = false;
		}
	}

	// if all of the remaining hostages are dead, they arent being rescued
	if (isAllDead)
		return false;

	return true;
}

// All hostages have been rescued or are dead
bool CSGameState::AreAllHostagesGone() const
{
	if (m_allHostagesRescued)
		return true;

	// do we know that all the hostages are dead
	for (int i = 0; i < m_hostageCount; i++)
	{
		const HostageInfo *info = &m_hostage[i];

		if (m_owner->m_iTeam == CT)
		{
			// CT's have perfect knowledge via their radar
			if (info->hostage->IsAlive())
				return false;
		}
		else
		{
			if (info->isValid && info->isAlive)
				return false;
		}
	}

	return true;
}

// Someone told us all the hostages are gone


switch (m_bombState)
	{
	case MOVING:
	{
		if (!m_lastSawBomber.HasStarted())
			return nullptr;

		return &m_bomberPos;
	}
	case LOOSE:
	{
		if (IsLooseBombLocationKnown())
			return &m_looseBombPos;

		return nullptr;
	}
	case PLANTED:
	{
		if (IsPlantedBombLocationKnown())
			return &m_plantedBombPos;

		return nullptr;
	}
	}

/*
*
*   respects for all of the code used other than the HL Engine and MODs
*   from Valve.  If you modify this file, you may extend this exception
*   to your version of the file, but you are not obligated to do so.  If
*   you do not wish to do so, delete this exception statement from your
*   version.
*
*/

#include "precompiled.h"

CSGameState::CSGameState(CCSBot *owner)
{
	m_owner = owner;
	m_isRoundOver = false;
	m_bombState = MOVING;

	m_lastSawBomber.Invalidate();
	m_lastSawLooseBomb.Invalidate();
	m_validateInterval.Invalidate();
	m_isPlantedBombPosKnown = false;
	m_plantedBombsite = UNKNOWN;

	m_bombsiteCount = 0;
	m_bombsiteSearchIndex = 0;

	for (int i = 0; i < MAX_HOSTAGES; i++)
	{
		HostageInfo *info = &m_hostage[i];

		info->hostage = nullptr;
		info->knownPos = Vector(0, 0, 0);
		info->isValid = false;
		info->isAlive = false;
		info->isFree = true;
	}
}

// Reset at round start
void CSGameState::Reset()
{
	int i;
	m_isRoundOver = false;

	// bomb
	m_bombState = MOVING;
	m_lastSawBomber.Invalidate();
	m_lastSawLooseBomb.Invalidate();
	m_bombsiteCount = TheCSBots()->GetZoneCount();

	m_isPlantedBombPosKnown = false;
	m_plantedBombsite = UNKNOWN;

	for (i = 0; i < m_bombsiteCount; i++)
	{
		m_isBombsiteClear[i] = false;
		m_bombsiteSearchOrder[i] = i;
	}

	// shuffle the bombsite search order
	// allows T's to plant at random site, and TEAM_CT's to search in a random order
	// NOTE: VS6 std::random_shuffle() doesn't work well with an array of two elements (most maps)
	for (i = 0; i < m_bombsiteCount; i++)
	{
		int swap = m_bombsiteSearchOrder[i];
		int rnd = RANDOM_LONG(i, m_bombsiteCount - 1);

		m_bombsiteSearchOrder[i] = m_bombsiteSearchOrder[rnd];
		m_bombsiteSearchOrder[rnd] = swap;
	}

	m_bombsiteSearchIndex = 0;
	InitializeHostageInfo();
}

// Update game state based on events we have received
void CSGameState::OnEvent(GameEventType event, CBaseEntity *pEntity, CBaseEntity *pOther)
{
	switch (event)
	{
	case EVENT_BOMB_PLANTED:
	{
		// change state - the event is announced to everyone
		SetBombState(PLANTED);

		// Terrorists always know where the bomb is
		if (m_owner->m_iTeam == TERRORIST && pOther)
		{
			UpdatePlantedBomb(&pOther->pev->origin);
		}
		break;
	}
	case EVENT_BOMB_DEFUSED:
		SetBombState(DEFUSED);
		break;
	case EVENT_BOMB_EXPLODED:
		SetBombState(EXPLODED);
		break;
	case EVENT_ALL_HOSTAGES_RESCUED:
		m_allHostagesRescued = true;
		break;
	case EVENT_TERRORISTS_WIN:
	case EVENT_CTS_WIN:
	case EVENT_ROUND_DRAW:
		m_isRoundOver = true;
		break;
	default:
		break;
	}
}

// True if round has been won or lost (but not yet reset)
bool CSGameState::IsRoundOver() const
{
	return m_isRoundOver;
}

void CSGameState::SetBombState(BombState state)
{
	// if state changed, reset "last seen" timestamps
	if (m_bombState != state)
	{
		m_bombState = state;
	}
}

void CSGameState::UpdateLooseBomb(const Vector *pos)
{
	m_looseBombPos = *pos;
	m_lastSawLooseBomb.Reset();

	// we saw the loose bomb, update our state
	SetBombState(LOOSE);
}

float CSGameState::TimeSinceLastSawLooseBomb() const
{
	return m_lastSawLooseBomb.GetElapsedTime();
}

bool CSGameState::IsLooseBombLocationKnown() const
{
	if (m_bombState != LOOSE)
		return false;

	return (m_lastSawLooseBomb.HasStarted()) ? true : false;
}

void CSGameState::UpdateBomber(const Vector *pos)
{
	m_bomberPos = *pos;
	m_lastSawBomber.Reset();

	// we saw the bomber, update our state
	SetBombState(MOVING);
}

float CSGameState::TimeSinceLastSawBomber() const
{
	return m_lastSawBomber.GetElapsedTime();
}

bool CSGameState::IsPlantedBombLocationKnown() const
{
	if (m_bombState != PLANTED)
		return false;

	return m_isPlantedBombPosKnown;
}

// Return the zone index of the planted bombsite, or UNKNOWN
int CSGameState::GetPlantedBombsite() const
{
	if (m_bombState != PLANTED)
		return UNKNOWN;

	return m_plantedBombsite;
}

// Return true if we are currently in the bombsite where the bomb is planted
bool CSGameState::IsAtPlantedBombsite() const
{
	if (m_bombState != PLANTED)
		return false;

	const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone(&m_owner->pev->origin);

	if (zone)
	{
		return (m_plantedBombsite == zone->m_index);
	}

	return false;
}

// Return the zone index of the next bombsite to search
int CSGameState::GetNextBombsiteToSearch()
{
	if (m_bombsiteCount <= 0)
		return 0;

	int i;
	// return next non-cleared bombsite index
	for (i = m_bombsiteSearchIndex; i < m_bombsiteCount; i++)
	{
		int z = m_bombsiteSearchOrder[i];
		if (!m_isBombsiteClear[z])
		{
			m_bombsiteSearchIndex = i;
			return z;
		}
	}

	// all the bombsites are clear, someone must have been mistaken - start search over
	for (i = 0; i < m_bombsiteCount; i++)
	{
		m_isBombsiteClear[i] = false;
	}

	m_bombsiteSearchIndex = 0;
	return GetNextBombsiteToSearch();
}

// Returns position of bomb in its various states (moving, loose, planted),
// or NULL if we don't know where the bomb is
const Vector *CSGameState::GetBombPosition() const
{

	return nullptr;
}

// We see the planted bomb at 'pos'
void CSGameState::UpdatePlantedBomb(const Vector *pos)
{
	const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone(pos);

	if (!zone)
	{
		CONSOLE_ECHO("ERROR: Bomb planted outside of a zone!\n");
		m_plantedBombsite = UNKNOWN;
	}
	else
	{
		m_plantedBombsite = zone->m_index;
	}

	m_plantedBombPos = *pos;
	m_isPlantedBombPosKnown = true;
	SetBombState(PLANTED);
}

// Someone told us where the bomb is planted
void CSGameState::MarkBombsiteAsPlanted(int zoneIndex)
{
	m_plantedBombsite = zoneIndex;
	SetBombState(PLANTED);
}

// Someone told us a bombsite is clear
void CSGameState::ClearBombsite(int zoneIndex)
{
	if (zoneIndex >= 0 && zoneIndex < m_bombsiteCount)
		m_isBombsiteClear[zoneIndex] = true;
}

bool CSGameState::IsBombsiteClear(int zoneIndex) const
{
	if (zoneIndex >= 0 && zoneIndex < m_bombsiteCount)
		return m_isBombsiteClear[zoneIndex];

	return false;
}

void CSGameState::InitializeHostageInfo()
{
	m_hostageCount = 0;
	m_allHostagesRescued = false;
	m_haveSomeHostagesBeenTaken = false;

	CHostage *pHostage = nullptr;
	while ((pHostage = UTIL_FindEntityByClassname(pHostage, "hostage_entity")))
	{
		if (m_hostageCount >= MAX_HOSTAGES)
			break;

		if (!pHostage->IsAlive())
			continue;

		m_hostage[m_hostageCount].hostage = pHostage;
		m_hostage[m_hostageCount].knownPos = pHostage->pev->origin;
		m_hostage[m_hostageCount].isValid = true;
		m_hostage[m_hostageCount].isAlive = true;
		m_hostage[m_hostageCount].isFree = true;
		m_hostageCount++;
	}
}

// Return the closest free and live hostage
// If we are a CT this information is perfect.
// Otherwise, this is based on our individual memory of the game state.
// If NULL is returned, we don't think there are any hostages left, or we dont know where they are.
// NOTE: a T can remember a hostage who has died.  knowPos will be filled in, but NULL will be
// returned, since CHostages get deleted when they die.
CHostage *CSGameState::GetNearestFreeHostage(Vector *knowPos) const
{
	if (!m_owner)
		return nullptr;

	CNavArea *startArea = m_owner->GetLastKnownArea();
	if (!startArea)
		return nullptr;

	CHostage *close = nullptr;
	const Vector *closePos = nullptr;
	float closeDistance = 9999999999.9f;

	for (int i = 0; i < m_hostageCount; i++)
	{
		CHostage *pHostage = m_hostage[i].hostage;
		const Vector *hostagePos = nullptr;

		if (m_owner->m_iTeam == CT)
		{
			// we know exactly where the hostages are, and if they are alive
			if (!m_hostage[i].hostage || !m_hostage[i].hostage->IsAlive())
				continue;

			if (m_hostage[i].hostage->IsFollowingSomeone())
				continue;

			hostagePos = &pHostage->pev->origin;
		}
		else
		{
			// use our memory of where we think the hostages are
			if (m_hostage[i].isValid == false)
				continue;

			hostagePos = &m_hostage[i].knownPos;
		}

		CNavArea *hostageArea = TheNavAreaGrid.GetNearestNavArea(hostagePos);
		if (hostageArea)
		{
			ShortestPathCost pc;
			float travelDistance = NavAreaTravelDistance(startArea, hostageArea, pc);

			if (travelDistance >= 0.0f && travelDistance < closeDistance)
			{
				closePos = hostagePos;
				closeDistance = travelDistance;
				close = pHostage;
			}
		}
	}

	// return where we think the hostage is
	if (knowPos && closePos)
	{
		knowPos = const_cast<Vector *>(closePos);
	}

	return close;
}

// Return the location of a "free" hostage, or NULL if we dont know of any
const Vector *CSGameState::GetRandomFreeHostagePosition()
{
	const Vector *freePos[MAX_HOSTAGES];
	int freeCount = 0;

	if (!m_owner)
		return nullptr;

	for (int i = 0; i < m_hostageCount; i++)
	{
		const HostageInfo *info = &m_hostage[i];
		const Vector *hostagePos = nullptr;

		if (m_owner->m_iTeam == CT)
		{
			// we know exactly where the hostages are, and if they are alive
			if (!info->hostage || !info->hostage->IsAlive())
				continue;

			// escorted hostages are not "free"
			if (info->hostage->IsFollowingSomeone())
				continue;

			freePos[freeCount++] = &info->hostage->pev->origin;
		}
		else
		{
			// use our memory of where we think the hostages are
			if (info->isValid == false)
				continue;

			freePos[freeCount++] = &info->knownPos;
		}
	}

	if (freeCount)
	{
		return freePos[RANDOM_LONG(0, freeCount - 1)];
	}

	return nullptr;
}

// If we can see any of the positions where we think a hostage is, validate it
// Return status of any changes (a hostage died or was moved)
CSGameState::ValidateStatusType CSGameState::ValidateHostagePositions()
{
	// limit how often we validate
	if (!m_validateInterval.IsElapsed())
		return NO_CHANGE;

	const float validateInterval = 0.5f;
	m_validateInterval.Start(validateInterval);

	// check the status of hostages
	unsigned char status = NO_CHANGE;

	int i;
	int startValidCount = 0;
	for (i = 0; i < m_hostageCount; i++)
	{
		if (m_hostage[i].isValid)
			startValidCount++;
	}

	for (i = 0; i < m_hostageCount; i++)
	{
		HostageInfo *info = &m_hostage[i];

		if (!info->hostage)
			continue;

		// if we can see a hostage, update our knowledge of it
		if (m_owner->IsVisible(&info->hostage->pev->origin, CHECK_FOV))
		{
			if (info->hostage->IsAlive())
			{
				// live hostage
				// if hostage is being escorted by a CT, we don't "see" it, we see the CT
				if (info->hostage->IsFollowingSomeone())
				{
					info->isValid = false;
				}
				else
				{
					info->knownPos = info->hostage->pev->origin;
					info->isValid = true;
				}
			}
			else
			{
				// dead hostage
				// if we thought it was alive, this is news to us
				if (info->isAlive)
					status |= HOSTAGE_DIED;

				info->isAlive = false;
				info->isValid = false;
			}

			continue;
		}

		// if we dont know where this hostage is, nothing to validate
		if (!info->isValid)
			continue;

		// can't directly see this hostage
		// check line of sight to where we think this hostage is, to see if we noticed that is has moved
		if (m_owner->IsVisible(&info->knownPos, CHECK_FOV))
		{
			// we can see where we thought the hostage was - verify it is still there and alive
			if (!info->hostage->IsAlive())
			{
				// since we have line of sight to an invalid hostage, it must be dead
				// discovered that hostage has been killed
				status |= HOSTAGE_DIED;

				info->isAlive = false;
				info->isValid = false;
				continue;
			}

			if (info->hostage->IsFollowingSomeone())
			{
				// discovered the hostage has been taken
				status |= HOSTAGE_GONE;
				info->isValid = false;
				continue;
			}

			const float tolerance = 50.0f;
			if ((info->hostage->pev->origin - info->knownPos).IsLengthGreaterThan(tolerance))
			{
				// discovered that hostage has been moved
				status |= HOSTAGE_GONE;
				info->isValid = false;
				continue;
			}
		}
	}

	int endValidCount = 0;
	for (i = 0; i < m_hostageCount; i++)
	{
		if (m_hostage[i].isValid)
			endValidCount++;
	}

	if (endValidCount == 0 && startValidCount > 0)
	{
		// we discovered all the hostages are gone
		status &= ~HOSTAGE_GONE;
		status |= HOSTAGES_ALL_GONE;
	}

	return static_cast<ValidateStatusType>(status);
}

// Return the nearest visible free hostage
// Since we can actually see any hostage we return, we know its actual position
CHostage *CSGameState::GetNearestVisibleFreeHostage() const
{
	CHostage *close = nullptr;
	float closeRangeSq = 999999999.9f;
	float rangeSq;

	Vector pos;

	for (int i = 0; i < m_hostageCount; i++)
	{
		const HostageInfo *info = &m_hostage[i];

		if (!info->hostage)
			continue;

		// if the hostage is dead or rescued, its not free
		if (!info->hostage->IsAlive())
			continue;

		// if this hostage is following someone, its not free
		if (info->hostage->IsFollowingSomeone())
			continue;

		// TODO: Use travel distance here
		pos = info->hostage->pev->origin + Vector(0, 0, HumanHeight * 0.75f);
		rangeSq = (pos - m_owner->pev->origin).LengthSquared();

		if (rangeSq < closeRangeSq)
		{
			if (!m_owner->IsVisible(&pos))
				continue;

			close = info->hostage;
			closeRangeSq = rangeSq;
		}
	}

	return close;
}

// Return true if there are no free hostages
bool CSGameState::AreAllHostagesBeingRescued() const
{
	// if the hostages have all been rescued, they are not being rescued any longer
	if (m_allHostagesRescued)
		return false;

	bool isAllDead = true;
	for (int i = 0; i < m_hostageCount; i++)
	{
		const HostageInfo *info = &m_hostage[i];

		if (m_owner->m_iTeam == CT)
		{
			// CT's have perfect knowledge via their radar
			if (info->hostage && info->hostage->IsAlive())
			{
				if (!info->hostage->IsFollowingSomeone())
					return false;

				isAllDead = false;
			}
		}
		else
		{
			if (info->isValid && info->isAlive)
				return false;

			if (info->isAlive)
				isAllDead = false;
		}
	}

	// if all of the remaining hostages are dead, they arent being rescued
	if (isAllDead)
		return false;

	return true;
}

// All hostages have been rescued or are dead
bool CSGameState::AreAllHostagesGone() const
{
	if (m_allHostagesRescued)
		return true;

	// do we know that all the hostages are dead
	for (int i = 0; i < m_hostageCount; i++)
	{
		const HostageInfo *info = &m_hostage[i];

		if (m_owner->m_iTeam == CT)
		{
			// CT's have perfect knowledge via their radar
			if (info->hostage->IsAlive())
				return false;
		}
		else
		{
			if (info->isValid && info->isAlive)
				return false;
		}
	}

	return true;
}

// Someone told us all the hostages are gone
void CSGameState::AllHostagesGone()
{
	for (int i = 0; i < m_hostageCount; i++)
		m_hostage[i].isValid = false;
}


void SPECIAL(unsigned long word)
{	//00(00)
    SPECIAL_T[word & 63](word);
}

#include "stdafx.h"
#include "erzdec.h"
/*
 mipsdis.c
 redistribute and modify at will, my name is spinout, keep it here.
*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "r3400i.h"

#define BSWAP32(x)	(unsigned long)((x & 0xFF000000) >> 24 | (x & 0xFF0000) >> 8 | (x & 0xFF00) << 8 | (x & 0xFF) << 24)

#define U32(x)  ((unsigned long)( (((unsigned char*)(x))[0]<<24)|(((unsigned char*)(x))[1]<<16)|(((unsigned char*)(x))[2]<<8)|((unsigned char*)(x))[3] ))
#define U16(x)  ((unsigned short)( ((*((unsigned char*)(x)))<<8)|((unsigned char*)(x))[1] ))
#define U8(x)	((unsigned char)((unsigned char*)(x))[0])
#define S32(x)  ((signed long)(U32(x)))
#define S16(x)  ((signed short)(U16(x)))
#define setU8(x,val)                   \
{                                       \
        (x)[0] = (val) & 0xFF;          \
}
#define setU32(x,val)                   \
{                                       \
        (x)[0] = ((val)>>24) & 0xFF;    \
        (x)[1] = ((val)>>16) & 0xFF;    \
        (x)[2] = ((val)>>8) & 0xFF;     \
        (x)[3] = (val) & 0xFF;          \
}
#define setU16(x,val)                   \
{                                       \
        (x)[0] = ((val)>>8) & 0xFF;     \
        (x)[1] = (val) & 0xFF;          \
}
typedef void (*Handler)( unsigned long word );
void run_op(unsigned long word);
unsigned char * buff;

signed long regs[32];
unsigned long pc;

void
do_branch(unsigned long word)
{
    unsigned char *dslot = buff + (pc & 0xFFFFFF) + 4;
    run_op( U32(dslot) );
    pc += getOFFSET(word);
}

void NONE(unsigned long word)
{
    printf("\a(Invalid opcode: %08X @ %08X)\n", word, pc);
    pc+=4;
}

void BLTZ(unsigned long word)
{
    if(regs[getRS(word)] < 0)do_branch(word);
    else pc+=4;
}

void BGEZ(unsigned long word)
{
    if(regs[getRS(word)] >= 0)do_branch(word);
    else pc+=4;
}

Handler REGIMM_T[32] = {
BLTZ,	BGEZ,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE
};

//SPECIAL op types
void SLL(unsigned long word)
{		//00 (00)
    if (word)
        regs[getRD(word)] = (((unsigned long)regs[getRT(word)] << (unsigned long)getSA(word)) & 0xFFFFFFFF);
    pc+=4;
}
void SRL(unsigned long word)
{		//02 (02)
    regs[getRD(word)] = (((unsigned long)regs[getRT(word)] >> (unsigned long)getSA(word)) & 0xFFFFFFFF);
    pc+=4;
}

void SLLV(unsigned long word)
{		//04 (04)
    regs[getRD(word)] = (((unsigned long)regs[getRT(word)] << (unsigned long)regs[getRS(word)]) & 0xFFFFFFFF);
    pc+=4;
}

void SRLV(unsigned long word)
{		//06 (06)
    regs[getRD(word)] = (((unsigned long)regs[getRT(word)] >> (unsigned long)regs[getRS(word)]) & 0xFFFFFFFF);
    pc+=4;
}

void JR(unsigned long word)
{		//08 (08)
    unsigned char *dslot = buff + (pc & 0xFFFFFF) + 4;
    run_op( U32(dslot) );
    pc = regs[getRS(word)];
}

void JALR(unsigned long word)
{		//08 (08)
    unsigned char *dslot = buff + (pc & 0xFFFFFF) + 4;
    run_op( U32(dslot) );
    regs[getRD(word)] = pc + 8;
    pc = regs[getRS(word)];
}

void ADD(unsigned long word)
{		//32 (20)
    regs[getRD(word)] = (signed long)regs[getRS(word)] + (signed long)regs[getRT(word)];
    pc+=4;
}

void ADDU(unsigned long word)
{		//33 (21)
    regs[getRD(word)] = (signed long)regs[getRS(word)] + (signed long)regs[getRT(word)];
    pc+=4;
}

void SUB(unsigned long word)
{		//34 (22)
    regs[getRD(word)] = (signed long)regs[getRS(word)] - (signed long)regs[getRT(word)];
    pc+=4;
}

void SUBU(unsigned long word)
{		//35 (23)
    regs[getRD(word)] = (unsigned long)regs[getRS(word)] - (signed long)regs[getRT(word)];
    pc+=4;
}

void AND(unsigned long word)
{		//36 (24)
    regs[getRD(word)] = regs[getRS(word)] & regs[getRT(word)];
    pc+=4;
}

void OR(unsigned long word)
{		//37 (25)
    regs[getRD(word)] = regs[getRS(word)] | regs[getRT(word)];
    pc+=4;
}

void XOR(unsigned long word)
{		//37 (25)
    regs[getRD(word)] = regs[getRS(word)] ^ regs[getRT(word)];
    pc+=4;
}

void NOR(unsigned long word)
{		//39 (27)
    regs[getRD(word)] = ~(regs[getRS(word)] | regs[getRT(word)]);
    pc+=4;
}

Handler SPECIAL_T[64] = {
SLL,	NONE,	SRL,	NONE,	SLLV,	NONE,	SRLV,	NONE,
JR,	JALR,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
ADD,	ADDU,	SUB,	SUBU,	AND,	OR,	XOR,	NOR,
NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE
};

//Main op types

void REGIMM(unsigned long word)
{	//01(01)
    REGIMM_T[getRT(word)](word);
}

void J(unsigned long word)
{		//02(02)
    unsigned char *dslot = buff + (pc & 0xFFFFFF) +4;
    run_op( U32(dslot) );
    pc = getTARGET(word)|0x80000000;
}

void JAL(unsigned long word)
{		//03(03)
    unsigned char *dslot = buff + (pc & 0xFFFFFF) + 4;
    regs[31] = pc + 8;
    run_op( U32(dslot) );
    pc = getTARGET(word)|0x80000000;
}

void BEQ(unsigned long word)
{		//04(04)
    if(regs[getRS(word)] == regs[getRT(word)])
        do_branch(word);
    else pc+=4;
}

void BNE(unsigned long word)
{		//05(05)
    if(regs[getRS(word)] != regs[getRT(word)])
        do_branch(word);
    else pc+=4;
}

void ADDI(unsigned long word)
{		//08(08)
    regs[getRT(word)] = regs[getRS(word)] + (signed short)getIMM(word);
    pc+=4;
}

void ADDIU(unsigned long word)
{	//09(09)
    regs[getRT(word)] = regs[getRS(word)] + (signed short)getIMM(word);
    pc+=4;
}

void ANDI(unsigned long word)
{		//12(0C)
    regs[getRT(word)] = regs[getRS(word)] & getIMM(word);
    pc+=4;
}

void ORI(unsigned long word)
{
    regs[getRT(word)] = regs[getRS(word)] | getIMM(word);
    pc+=4;
}

void LUI(unsigned long word)
{
    regs[getRT(word)] = ((signed short)getIMM(word)) << 16;
    pc+=4;
}

void LH(unsigned long word)
{
    unsigned addr = (signed short)getIMM(word) + regs[getBASE(word)];
    regs[getRT(word)] = S16(buff + (addr & 0xFFFFFF));
    pc+=4;
}

void LW(unsigned long word)
{		//35(23)
    unsigned addr = (signed short)getIMM(word) + regs[getBASE(word)];
    regs[getRT(word)] = S32(buff + (addr & 0xFFFFFF));
    pc+=4;
}

void LBU(unsigned long word)
{		//36(24)
    unsigned addr = (signed short)getIMM(word) + regs[getBASE(word)];
    regs[getRT(word)] = U8(buff + (addr & 0xFFFFFF));
    pc+=4;
}

void LHU(unsigned long word)
{
    unsigned addr = (signed short)getIMM(word) + regs[getBASE(word)];
    regs[getRT(word)] = U16(buff + (addr & 0xFFFFFF));
    pc+=4;
}

void SB(unsigned long word)
{		//40(28)
    unsigned addr = (signed short)getIMM(word) + regs[getBASE(word)];
    setU8(buff + (addr&0xFFFFFF), regs[getRT(word)]);
    pc+=4;
}

void SH(unsigned long word)
{
    unsigned addr = (signed short)getIMM(word) + regs[getBASE(word)];
    setU16(buff + (addr&0xFFFFFF), regs[getRT(word)]);
    pc+=4;
}

void SW(unsigned long word)
{		//43(2B)
    unsigned char *addr = buff + (((signed short)getIMM(word) + regs[getBASE(word)]) & 0xFFFFFF);
    setU32(addr, regs[getRT(word)]);
    pc+=4;
}

Handler MAIN_T[64] =
{
SPECIAL,REGIMM, J,	JAL,	BEQ,	BNE,	NONE,	NONE,
ADDI,	ADDIU,	NONE,	NONE,	ANDI,	ORI,	NONE,	LUI,
NONE,   NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
NONE,   NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
NONE,	LH,	NONE,	LW,	LBU,	LHU,	NONE,	NONE,
SB,	SH,	NONE,	SW,	NONE,	NONE,	NONE,	NONE,
NONE,   NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
NONE,   NONE,	NONE,	NONE,	NONE,	NONE,	NONE,	NONE,
};

void
run_op(unsigned long word)
{
    MAIN_T[ word >> 26 ](word);
}

int ERZ::decode(unsigned char * src, int filesize, unsigned char* result, int& fileSizeCompressed)
{
	int length = -1;
    int wordc=0;

    unsigned return_addr;

    int address=0;

    buff=(unsigned char*)malloc(0x300000);
	memset(buff, 0, 0x300000);
    memcpy(&buff[0x2000], src, filesize);

	// Get the ERZ header from the buffer.
	ERZHeader *pHeader = (ERZHeader*)src;

	// Verify the header magic is correct.
	if (pHeader->Magic[0] != 'E' || pHeader->Magic[1] != 'R' || pHeader->Magic[2] != 'Z')
	{
		// Header signature is invalid.
		free(buff);
		return -1;
	}

    memset((void*)regs, 0, sizeof(regs));
    return_addr = regs[31] = 0xdeadbeef;

	// Setup parameters for decompression.
    regs[4] = 0x80002000;							// $a0 = pCompressedData
    regs[5] = 0x80100000;							// $a1 = pDecompressedData
	regs[6] = BSWAP32(pHeader->DecompressedSize);	// $a2 = dwDecompressedSize
	regs[7] = BSWAP32(pHeader->CompressedSize);		// $a3 = dwCompressedSize
    regs[29] =0x80280000;	//stack

    #include "func.h"
	memcpy(buff + (ERZ1_EP&0xFFFFFF), erz1, sizeof(erz1));
    memcpy(buff + (ERZ2_EP&0xFFFFFF), erz2, sizeof(erz2));

	// Check the compression type and handle accordingle.
	if (pHeader->CompressionType == 1)
	{
		pc = ERZ1_EP;
	}
	else if (pHeader->CompressionType == 2)
	{
		pc = ERZ2_EP;
	}
	else
	{
		free(buff);
		return -1;
	}

	for (; pc != return_addr;)
	{
		run_op(U32(buff + (pc & 0xFFFFFF)));
		regs[0] = 0;
	}

	memcpy(result, (buff + 0x100000), BSWAP32(pHeader->DecompressedSize));

	// Free temp buffer.
    free(buff);

	// Return the length of the decompressed data.
	return BSWAP32(pHeader->DecompressedSize);
}


void IFCImp::ShowAbout(HWND /*hWnd*/) {}

/********************************************************************************
 *                                                                              *
 * This file is part of IfcOpenShell.                                           *
 *                                                                              *
 *                                                                              *
 ********************************************************************************/

#include <map>
#include <set>

#include <stdmat.h>
#include <istdplug.h>

#include "IfcMax.h"

#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
#include "../ifcgeom_schema_agnostic/IfcGeomMaterial.h"
#include "../ifcgeom/IfcGeomElement.h"

static const int NUM_MATERIAL_SLOTS = 24;

BOOL WINAPI DllMain(HINSTANCE /*hinstDLL*/, ULONG /*fdwReason*/, LPVOID /*lpvReserved*/) {
    static int controlsInit = false;
	if (!controlsInit) {
		controlsInit = true;
		InitCommonControls();
	}
	return TRUE;
}

static class IFCImpClassDesc :public ClassDesc {
public:
    int                     IsPublic() { return 1; }
    void *                  Create(BOOL /*loading = FALSE*/) { return new IFCImp; }
    const TCHAR *			ClassName() { return _T("IFCImp"); }
    SClass_ID               SuperClassID() { return SCENE_IMPORT_CLASS_ID; }
    Class_ID                ClassID() { return Class_ID(0x3f230dbf, 0x5b3015c2); }
    const TCHAR*			Category() { return _T("Chrutilities"); }
} IFCImpDesc;

#define DLLEXPORT __declspec(dllexport)

extern "C" {

DLLEXPORT const TCHAR* LibDescription() {
    return _T("IfcOpenShell IFC Importer");
}

DLLEXPORT int LibNumberClasses() { return 1; }

DLLEXPORT ClassDesc* LibClassDesc(int i) {
    return i == 0 ? &IFCImpDesc : 0;
}

DLLEXPORT ULONG LibVersion() {
    return VERSION_3DSMAX;
}

} // extern "C"

int IFCImp::ExtCount() { return 1; }

const TCHAR * IFCImp::Ext(int n) {
	return n == 0 ? _T("IFC") : _T("");
}

const TCHAR * IFCImp::LongDesc() {
	return _T("IfcOpenShell IFC Importer for 3ds Max");
}

const TCHAR * IFCImp::ShortDesc() {
	return _T("Industry Foundation Classes");
}

const TCHAR * IFCImp::AuthorName() {
	return _T("Thomas Krijnen");
}

const TCHAR * IFCImp::CopyrightMessage() {
	return _T("Copyright (c) 2011-2016 IfcOpenShell");
}

const TCHAR * IFCImp::OtherMessage1() {
	return _T("");
}

const TCHAR * IFCImp::OtherMessage2() {
	return _T("");
}

unsigned int IFCImp::Version() {
	return 12;
}

//static BOOL CALLBACK AboutBoxDlgProc(HWND /*hWnd*/, UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lParam*/) {

DWORD WINAPI fn(LPVOID /*arg*/) { return 0; }

#if MAX_RELEASE > 14000
# define S(x) (TSTR::FromCStr(x.c_str()))
#elif defined(_UNICODE)
# define S(x) (WStr(x.c_str()))
#else
# define S(x) (CStr(x.c_str()))
#endif

static Mtl* FindMaterialByName(MtlBaseLib* library, const std::string& material_name) {
    TSTR mat_name = S(material_name);
	const int mat_index = library->FindMtlByName(mat_name);
	Mtl* m = 0;
	if (mat_index != -1) {
		m = static_cast<Mtl*>((*library)[mat_index]);
	}
	return m;
}

static Mtl* FindOrCreateMaterial(MtlBaseLib* library, Interface* max_interface, int& slot, const IfcGeom::Material& material) {
	Mtl* m = FindMaterialByName(library, material.name());
	if (m == 0) {
		StdMat2* stdm = NewDefaultStdMat();
		const TimeValue t = -1;
		if (material.hasDiffuse()) {
			const double* diffuse = material.diffuse();
			stdm->SetDiffuse(Color(diffuse[0], diffuse[1], diffuse[2]),t);
		}
		if (material.hasSpecular()) {
			const double* specular = material.specular();
			stdm->SetSpecular(Color(specular[0], specular[1], specular[2]),t);
		}
		if (material.hasSpecularity()) {
			stdm->SetShininess((float)material.specularity(), t);
		}
		if (material.hasTransparency()) {
			stdm->SetOpacity(1.0f - (float)material.transparency(), t);
		}
		m = stdm;
		m->SetName(S(material.name()));
		library->Add(m);
		if (slot < NUM_MATERIAL_SLOTS) {
			max_interface->PutMtlToMtlEditor(m,slot++);
		}
	}
	return m;
}

static Mtl* ComposeMultiMaterial(std::map<std::vector<std::string>, Mtl*>& multi_mats, MtlBaseLib* library,
    Interface* max_interface, int& slot, const std::vector<IfcGeom::Material>& materials,
    const std::string& object_type, const std::vector<int>& material_ids)
{
	std::vector<std::string> material_names;
	bool needs_default = std::find(material_ids.begin(), material_ids.end(), -1) != material_ids.end();
	if (needs_default) {
		material_names.push_back(object_type);
	}
	for (auto it = materials.begin(); it != materials.end(); ++it) {
		material_names.push_back(it->name());
	}
	Mtl* default_material = 0;
	if (needs_default) {
		default_material = FindMaterialByName(library, object_type);
		if (default_material == 0) {
			default_material = NewDefaultStdMat();
			default_material->SetName(S(object_type));
			library->Add(default_material);
			if (slot < NUM_MATERIAL_SLOTS) {
				max_interface->PutMtlToMtlEditor(default_material, slot++);
			}
		}
	}
	if (material_names.size() == 1) {
		if (needs_default) {
			return default_material;
		} else {
			return FindOrCreateMaterial(library, max_interface, slot, *materials.begin());
		}
	}
	std::map<std::vector<std::string>, Mtl*>::const_iterator i = multi_mats.find(material_names);
	if (i != multi_mats.end()) {
		return i->second;
	}
	MultiMtl* multi_mat = NewDefaultMultiMtl();
	multi_mat->SetNumSubMtls((int)material_names.size());
	int mtl_id = 0;
	if (needs_default) {
		multi_mat->SetSubMtlAndName(mtl_id ++, default_material, default_material->GetName());
	}
	for (auto it = materials.begin(); it != materials.end(); ++it) {
		Mtl* mtl = FindOrCreateMaterial(library, max_interface, slot, *it);
		multi_mat->SetSubMtl(mtl_id ++, mtl);
	}
	library->Add(multi_mat);
	if (slot < NUM_MATERIAL_SLOTS) {
		max_interface->PutMtlToMtlEditor(multi_mat,slot++);
	}
	multi_mats.insert(std::pair<std::vector<std::string>, Mtl*>(material_names, multi_mat));
	return multi_mat;
}

int IFCImp::DoImport(const TCHAR *name, ImpInterface *impitfc, Interface *itfc, BOOL /*suppressPrompts*/) {

	IfcGeom::IteratorSettings settings;
    settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false);
    settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, true);
    settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, true);

#ifdef _UNICODE
	int fn_buffer_size = WideCharToMultiByte(CP_UTF8, 0, name, -1, 0, 0, 0, 0);
	char* fn_mb = new char[fn_buffer_size];
	WideCharToMultiByte(CP_UTF8, 0, name, -1, fn_mb, fn_buffer_size, 0, 0);
#else
	const char* fn_mb = name;
#endif

	IfcParse::IfcFile file(fn_mb);
	IfcGeom::Iterator<float> iterator(settings, &file);
    delete fn_mb;
	if (!iterator.initialize()) return false;

	itfc->ProgressStart(_T("Importing file..."), TRUE, fn, NULL);

	MtlBaseLib* mats = itfc->GetSceneMtls();
	int slot = mats->Count();

	std::map<std::vector<std::string>, Mtl*> material_cache;

	do{
		const IfcGeom::TriangulationElement<float>* o = static_cast<const IfcGeom::TriangulationElement<float>*>(iterator.get());

		TSTR o_type = S(o->type());
		TSTR o_guid = S(o->guid());

		Mtl *m = ComposeMultiMaterial(material_cache, mats, itfc, slot, o->geometry().materials(), o->type(), o->geometry().material_ids());

		TriObject* tri = CreateNewTriObject();

		const int numVerts = (int)o->geometry().verts().size()/3;
		tri->mesh.setNumVerts(numVerts);
		for( int i = 0; i < numVerts; i ++ ) {
			tri->mesh.setVert(i,o->geometry().verts()[3*i+0],o->geometry().verts()[3*i+1],o->geometry().verts()[3*i+2]);
		}
		const int numFaces = (int)o->geometry().faces().size()/3;
		tri->mesh.setNumFaces(numFaces);

		bool needs_default = std::find(o->geometry().material_ids().begin(), o->geometry().material_ids().end(), -1) != o->geometry().material_ids().end();

		typedef std::pair<int, int> edge_t;

		std::set<edge_t> face_boundaries;
		for(std::vector<int>::const_iterator it = o->geometry().edges().begin(); it != o->geometry().edges().end();) {
			const int v1 = *it++;
			const int v2 = *it++;

			const edge_t e((std::min)(v1, v2), (std::max)(v1, v2));
			face_boundaries.insert(e);
		}

		for( int i = 0; i < numFaces; i ++ ) {
			const int v1 = o->geometry().faces()[3*i+0];
			const int v2 = o->geometry().faces()[3*i+1];
			const int v3 = o->geometry().faces()[3*i+2];

			const edge_t e1((std::min)(v1, v2), (std::max)(v1, v2));
			const edge_t e2((std::min)(v2, v3), (std::max)(v2, v3));
			const edge_t e3((std::min)(v3, v1), (std::max)(v3, v1));

			const bool b1 = face_boundaries.find(e1) != face_boundaries.end();
			const bool b2 = face_boundaries.find(e2) != face_boundaries.end();
			const bool b3 = face_boundaries.find(e3) != face_boundaries.end();

			tri->mesh.faces[i].setVerts(v1, v2, v3);
			tri->mesh.faces[i].setEdgeVisFlags(b1, b2, b3);

			MtlID mtlid = (MtlID)o->geometry().material_ids()[i];
			if (needs_default) {
				mtlid ++;
			}
			tri->mesh.faces[i].setMatID(mtlid);
		}

		tri->mesh.buildNormals();
		// Either use this or undefine the FACESETS_AS_COMPOUND option in IfcGeom.h to have
		// properly oriented normals. Using only the line below will result in a consistent
		// orientation of normals across shells, but not always oriented towards the
		tri->mesh.BuildStripsAndEdges();
		tri->mesh.InvalidateTopologyCache();
		tri->mesh.InvalidateGeomCache();

		ImpNode* node = impitfc->CreateNode();
		node->Reference(tri);
		node->SetName(o_guid);
		node->GetINode()->Hide(o->type() == "IfcOpeningElement" || o->type() == "IfcSpace");
		if (m) {
			node->GetINode()->SetMtl(m);
		}
		const std::vector<float>& matrix_data = o->transformation().matrix().data();
		node->SetTransform(0,Matrix3 ( Point3(matrix_data[0],matrix_data[1],matrix_data[2]),Point3(matrix_data[3],matrix_data[4],matrix_data[5]),
			Point3(matrix_data[6],matrix_data[7],matrix_data[8]),Point3(matrix_data[9],matrix_data[10],matrix_data[11]) ));
		impitfc->AddNodeToScene(node);

		itfc->ProgressUpdate(iterator.progress(), true, _T(""));

	} while (iterator.next());

	itfc->ProgressEnd();

	return true;
}


switch (uMsg)
	{
	case WM_DPICHANGED:
	case WM_SETTINGCHANGE:
	case WM_THEMECHANGED:
	case WM_DISPLAYCHANGE:
		LoadThemedIcon();

		if (m_CurrentlyShowing)
		{
			Notify(NIM_MODIFY);
		}
		break;
	default:
		if (uMsg == m_TaskbarCreatedMessage)
		{
			// it's not actually showing anymore, explorer restarted
			m_CurrentlyShowing = false;

			if (m_ShowPreference)
			{
				Show();
			}

			return 0;
		}
	}

#include "trayicon.hpp"
#include <shellapi.h>

#include "appinfo.hpp"
#include "constants.hpp"
#include "../localization.hpp"
#include "../../ProgramLog/error/errno.hpp"
#include "../../ProgramLog/error/std.hpp"
#include "../../ProgramLog/error/win32.hpp"

void TrayIcon::LoadThemedIcon()
{
	const wchar_t *icon = m_whiteIconResource;
	if (m_Ssudm && !m_Ssudm())
	{
		icon = m_darkIconResource;
	}

	if (const HRESULT hr = LoadIconMetric(hinstance(), icon, LIM_SMALL, m_Icon.put()); SUCCEEDED(hr))
	{
		m_IconData.uFlags |= NIF_ICON;
		m_IconData.hIcon = m_Icon.get();
	}
	else
	{
		m_IconData.uFlags &= ~NIF_ICON;
		m_Icon.reset();
		HresultHandle(hr, spdlog::level::warn, L"Failed to load tray icon.");
	}
}

bool TrayIcon::Notify(DWORD message, NOTIFYICONDATA *data)
{
	if (Shell_NotifyIcon(message, data ? data : &m_IconData))
	{
		return true;
	}
	else
	{
		MessagePrint(spdlog::level::info, L"Failed to notify shell.");

		return false;
	}
}

LRESULT TrayIcon::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam)
{

	return MessageWindow::MessageHandler(uMsg, wParam, lParam);
}

std::optional<RECT> TrayIcon::GetTrayRect()
{
	NOTIFYICONIDENTIFIER id = { sizeof(id) };
#ifdef SIGNED_BUILD
	id.guidItem = m_IconData.guidItem;
#else
	id.hWnd = m_IconData.hWnd;
	id.uID = m_IconData.uID;
	id.guidItem = GUID_NULL;
#endif

	RECT rect{};
	const HRESULT hr = Shell_NotifyIconGetRect(&id, &rect);
	if (SUCCEEDED(hr))
	{
		return rect;
	}
	else
	{
		HresultHandle(hr, spdlog::level::warn, L"Failed to get tray rect");
		return std::nullopt;
	}
}

TrayIcon::TrayIcon(const GUID &iconId, const wchar_t *whiteIconResource,
	const wchar_t *darkIconResource, PFN_SHOULD_SYSTEM_USE_DARK_MODE ssudm) :
	m_IconData {
		.cbSize = sizeof(m_IconData),
		.hWnd = m_WindowHandle,
		.uFlags = NIF_MESSAGE | NIF_TIP,
		.uCallbackMessage = TRAY_CALLBACK,
		.szTip = APP_NAME,
		.uVersion = NOTIFYICON_VERSION_4
	},
	m_whiteIconResource(whiteIconResource),
	m_darkIconResource(darkIconResource),
	m_ShowPreference(false),
	m_CurrentlyShowing(false),
	m_TaskbarCreatedMessage(Window::RegisterMessage(WM_TASKBARCREATED)),
	m_Ssudm(ssudm)
{
#ifdef SIGNED_BUILD
	m_IconData.uFlags |= NIF_GUID;
	m_IconData.guidItem = iconId;
#else
	// good enough method to get an unique id
	m_IconData.uID = iconId.Data1;
#endif

	LoadThemedIcon();

#ifdef SIGNED_BUILD
	// Clear icon from a previous instance that didn't cleanly exit.
	// Errors if instance cleanly exited, so avoid logging it.
	// Only works when using GUIDs.
	Shell_NotifyIcon(NIM_DELETE, &m_IconData);
#endif
}

void TrayIcon::Show()
{
	m_ShowPreference = true;
	if (!m_CurrentlyShowing && Notify(NIM_ADD))
	{
		Notify(NIM_SETVERSION);
		m_CurrentlyShowing = true;
	}
}

void TrayIcon::Hide()
{
	m_ShowPreference = false;
	if (m_CurrentlyShowing && Notify(NIM_DELETE))
	{
		m_CurrentlyShowing = false;
	}
}

void TrayIcon::SendNotification(uint16_t textResource, DWORD infoFlags)
{
	if (m_CurrentlyShowing)
	{
		// copy the data because if explorer restarts or the theme/settings change we don't want to re-send the notification
		auto data = m_IconData;
		data.uFlags |= NIF_INFO;
		data.dwInfoFlags = infoFlags;
		// don't set szInfoTitle, the OS will show the app name already.

		Localization::LoadLocalizedResourceString(textResource, hinstance()).copy(data.szInfo, std::size(data.szInfo));

		Notify(NIM_MODIFY, &data);
	}
}

TrayIcon::~TrayIcon() noexcept(false)
{
	Hide();
}


int LastRemaining_Solution3(int n, int m) {
        if (n <= 0) return -1;
        int index = 0;
        for (int i = 2; i <= n; ++i) {
            index = (index + m) % i;
        }
        return index;
    }

#include <vector>
#include <list>

using namespace std;

class Solution {
public:
    int LastRemaining_Solution(int n, int m) {
        if (n <= 0) return -1;
        list<int> kids;
        for (int i = 0; i < n; ++i) {
            kids.push_back(i);
        }
        int index = -1; //初始化为-1
        while (kids.size() > 1) {
            int count = 0;
            while (count < m) {
                count++; //数数 从0开始数到m-1
                index++; //记录当前数到的位置
                if (index == kids.size()) {
                    index = 0; //已经数完一圈重新开始
                }
            }
            auto it = kids.begin();
            std::advance(it, index); // 让it向后移动index个位置
            kids.erase(it);
            index--;
        }
        return kids.back();
    }

    // 递归
    int LastRemaining_Solution2(int n, int m) {

        {
            if (n == 0)
                return -1;
            if (n == 1)
                return 0;
            else
                return (LastRemaining_Solution(n - 1, m) + m) % n;
        }
    }

    // 迭代


void RespaOMP::init()
{
  Respa::init();

  if (atom->torque)
    error->all(FLERR,"Extended particles are not supported by respa/omp\n");
}

/* ----------------------------------------------------------------------
   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
   http://lammps.sandia.gov, Sandia National Laboratories
   Steve Plimpton, example@nnthink.com

   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains

   See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */

/* ----------------------------------------------------------------------
   Contributing authors: Mark Stevens (SNL), Paul Crozier (SNL)
------------------------------------------------------------------------- */

#include <stdlib.h>
#include <string.h>
#include "respa_omp.h"
#include "neighbor.h"
#include "domain.h"
#include "comm.h"
#include "atom.h"
#include "force.h"
#include "pair.h"
#include "bond.h"
#include "angle.h"
#include "dihedral.h"
#include "improper.h"
#include "kspace.h"
#include "output.h"
#include "update.h"
#include "modify.h"
#include "compute.h"
#include "fix_respa.h"
#include "timer.h"
#include "memory.h"
#include "error.h"

#if defined(_OPENMP)
#include <omp.h>
#endif

using namespace LAMMPS_NS;

/* ---------------------------------------------------------------------- */

RespaOMP::RespaOMP(LAMMPS *lmp, int narg, char **arg)
  : Respa(lmp, narg, arg),ThrOMP(lmp, THR_INTGR)
{
}

/* ----------------------------------------------------------------------
   initialization before run
------------------------------------------------------------------------- */

/* ----------------------------------------------------------------------
   setup before run
------------------------------------------------------------------------- */

void RespaOMP::setup()
{
  if (comm->me == 0 && screen) {
    fprintf(screen,"Setting up r-RESPA/omp run ...\n");
    fprintf(screen,"  Unit style    : %s\n", update->unit_style);
    fprintf(screen,"  Current step  : " BIGINT_FORMAT "\n", update->ntimestep);
    fprintf(screen,"  Time steps    :");
    for (int ilevel=0; ilevel < nlevels; ++ilevel)
      fprintf(screen," %d:%g",ilevel+1, step[ilevel]);
    fprintf(screen,"\n  r-RESPA fixes :");
    for (int l=0; l < modify->n_post_force_respa; ++l) {
      Fix *f = modify->fix[modify->list_post_force_respa[l]];
      if (f->respa_level >= 0)
        fprintf(screen," %d:%s[%s]",
                MIN(f->respa_level+1,nlevels),f->style,f->id);
    }
    fprintf(screen,"\n");
    timer->print_timeout(screen);
  }

  update->setupflag = 1;

  // setup domain, communication and neighboring
  // acquire ghosts
  // build neighbor lists

  atom->setup();
  modify->setup_pre_exchange();
  if (triclinic) domain->x2lamda(atom->nlocal);
  domain->pbc();
  domain->reset_box();
  comm->setup();
  if (neighbor->style) neighbor->setup_bins();
  comm->exchange();
  if (atom->sortfreq > 0) atom->sort();
  comm->borders();
  if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost);
  domain->image_check();
  domain->box_too_small_check();
  modify->setup_pre_neighbor();
  neighbor->build();
  modify->setup_post_neighbor();
  neighbor->ncalls = 0;

  // compute all forces

  ev_set(update->ntimestep);

  for (int ilevel = 0; ilevel < nlevels; ilevel++) {
    force_clear(newton[ilevel]);
    modify->setup_pre_force_respa(vflag,ilevel);

    if (nhybrid_styles > 0) {
      set_compute_flags(ilevel);
      force->pair->compute(eflag,vflag);
    }
    if (level_pair == ilevel && pair_compute_flag)
      force->pair->compute(eflag,vflag);
    if (level_inner == ilevel && pair_compute_flag)
      force->pair->compute_inner();
    if (level_middle == ilevel && pair_compute_flag)
      force->pair->compute_middle();
    if (level_outer == ilevel && pair_compute_flag)
      force->pair->compute_outer(eflag,vflag);
    if (level_bond == ilevel && force->bond)
      force->bond->compute(eflag,vflag);
    if (level_angle == ilevel && force->angle)
      force->angle->compute(eflag,vflag);
    if (level_dihedral == ilevel && force->dihedral)
      force->dihedral->compute(eflag,vflag);
    if (level_improper == ilevel && force->improper)
      force->improper->compute(eflag,vflag);
    if (level_kspace == ilevel && force->kspace) {
      force->kspace->setup();
      if (kspace_compute_flag) force->kspace->compute(eflag,vflag);
    }

    // reduce forces from per-thread arrays, if needed
    if (!fix->get_reduced()) {
      const int nall = atom->nlocal + atom->nghost;
      const int nthreads = comm->nthreads;
#if defined(_OPENMP)
#pragma omp parallel default(none)
#endif
      {
#if defined(_OPENMP)
	int tid = omp_get_thread_num();
#else
	int tid = 0;
#endif
	data_reduce_thr(atom->f[0], nall, nthreads, 3, tid);
      }
      fix->did_reduce();
    }

    modify->pre_reverse(eflag,vflag);
    if (newton[ilevel]) comm->reverse_comm();
    copy_f_flevel(ilevel);
  }

  sum_flevel_f();
  modify->setup(vflag);
  output->setup();
  update->setupflag = 0;
}

/* ----------------------------------------------------------------------
   setup without output
   flag = 0 = just force calculation
   flag = 1 = reneighbor and force calculation
------------------------------------------------------------------------- */

void RespaOMP::setup_minimal(int flag)
{
  update->setupflag = 1;

  // setup domain, communication and neighboring
  // acquire ghosts
  // build neighbor lists

  if (flag) {
    modify->setup_pre_exchange();
    if (triclinic) domain->x2lamda(atom->nlocal);
    domain->pbc();
    domain->reset_box();
    comm->setup();
    if (neighbor->style) neighbor->setup_bins();
    comm->exchange();
    comm->borders();
    if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost);
    domain->image_check();
    domain->box_too_small_check();
    modify->setup_pre_neighbor();
    neighbor->build();
    modify->setup_post_neighbor();
    neighbor->ncalls = 0;
  }

  // compute all forces

  ev_set(update->ntimestep);

  for (int ilevel = 0; ilevel < nlevels; ilevel++) {
    force_clear(newton[ilevel]);
    modify->setup_pre_force_respa(vflag,ilevel);

    if (nhybrid_styles > 0) {
      set_compute_flags(ilevel);
      force->pair->compute(eflag,vflag);
    }

    if (level_pair == ilevel && pair_compute_flag)
      force->pair->compute(eflag,vflag);
    if (level_inner == ilevel && pair_compute_flag)
      force->pair->compute_inner();
    if (level_middle == ilevel && pair_compute_flag)
      force->pair->compute_middle();
    if (level_outer == ilevel && pair_compute_flag)
      force->pair->compute_outer(eflag,vflag);
    if (level_bond == ilevel && force->bond)
      force->bond->compute(eflag,vflag);
    if (level_angle == ilevel && force->angle)
      force->angle->compute(eflag,vflag);
    if (level_dihedral == ilevel && force->dihedral)
      force->dihedral->compute(eflag,vflag);
    if (level_improper == ilevel && force->improper)
      force->improper->compute(eflag,vflag);
    if (level_kspace == ilevel && force->kspace) {
      force->kspace->setup();
      if (kspace_compute_flag) force->kspace->compute(eflag,vflag);
    }

    // reduce forces from per-thread arrays, if needed
    if (!fix->get_reduced()) {
      const int nall = atom->nlocal + atom->nghost;
      const int nthreads = comm->nthreads;
#if defined(_OPENMP)
#pragma omp parallel default(none)
#endif
      {
#if defined(_OPENMP)
	int tid = omp_get_thread_num();
#else
	int tid = 0;
#endif
	data_reduce_thr(atom->f[0], nall, nthreads, 3, tid);
      }
      fix->did_reduce();
    }

    modify->pre_reverse(eflag,vflag);
    if (newton[ilevel]) comm->reverse_comm();
    copy_f_flevel(ilevel);
  }

  sum_flevel_f();
  modify->setup(vflag);
  update->setupflag = 0;
}

/* ---------------------------------------------------------------------- */

void RespaOMP::recurse(int ilevel)
{
  copy_flevel_f(ilevel);

  for (int iloop = 0; iloop < loop[ilevel]; iloop++) {

    timer->stamp();
    modify->initial_integrate_respa(vflag,ilevel,iloop);
    if (modify->n_post_integrate_respa)
      modify->post_integrate_respa(ilevel,iloop);
    timer->stamp(Timer::MODIFY);

    // at outermost level, check on rebuilding neighbor list
    // at innermost level, communicate
    // at middle levels, do nothing

    if (ilevel == nlevels-1) {
      int nflag = neighbor->decide();
      if (nflag) {
        if (modify->n_pre_exchange) {
          timer->stamp();
          modify->pre_exchange();
          timer->stamp(Timer::MODIFY);
        }
        if (triclinic) domain->x2lamda(atom->nlocal);
        domain->pbc();
        if (domain->box_change) {
          domain->reset_box();
          comm->setup();
          if (neighbor->style) neighbor->setup_bins();
        }
        timer->stamp();
        comm->exchange();
        if (atom->sortfreq > 0 &&
            update->ntimestep >= atom->nextsort) atom->sort();
        comm->borders();
        if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost);
        timer->stamp(Timer::COMM);
        if (modify->n_pre_neighbor) {
          modify->pre_neighbor();
          timer->stamp(Timer::MODIFY);
        }
        neighbor->build();
        timer->stamp(Timer::NEIGH);
        if (modify->n_post_neighbor) {
          modify->post_neighbor();
          timer->stamp(Timer::MODIFY);
        }
      } else if (ilevel == 0) {
        timer->stamp();
        comm->forward_comm();
        timer->stamp(Timer::COMM);
      }

    } else if (ilevel == 0) {
      timer->stamp();
      comm->forward_comm();
      timer->stamp(Timer::COMM);
    }

    // rRESPA recursion thru all levels
    // this used to be before neigh list build,
    // which prevented per-atom energy/stress being tallied correctly
    // b/c atoms migrated to new procs between short/long force calls
    // now they migrate at very start of rRESPA timestep, before all forces

    if (ilevel) recurse(ilevel-1);

    // force computations
    // important that ordering is same as Verlet
    // so that any order dependencies are the same
    // when potentials are invoked at same level

    force_clear(newton[ilevel]);
    if (modify->n_pre_force_respa) {
      timer->stamp();
      modify->pre_force_respa(vflag,ilevel,iloop);
      timer->stamp(Timer::MODIFY);
    }

    timer->stamp();
    if (nhybrid_styles > 0) {
      set_compute_flags(ilevel);
      force->pair->compute(eflag,vflag);
      timer->stamp(Timer::PAIR);
    }
    if (level_pair == ilevel && pair_compute_flag) {
      force->pair->compute(eflag,vflag);
      timer->stamp(Timer::PAIR);
    }
    if (level_inner == ilevel && pair_compute_flag) {
      force->pair->compute_inner();
      timer->stamp(Timer::PAIR);
    }
    if (level_middle == ilevel && pair_compute_flag) {
      force->pair->compute_middle();
      timer->stamp(Timer::PAIR);
    }
    if (level_outer == ilevel && pair_compute_flag) {
      force->pair->compute_outer(eflag,vflag);
      timer->stamp(Timer::PAIR);
    }
    if (level_bond == ilevel && force->bond) {
      force->bond->compute(eflag,vflag);
      timer->stamp(Timer::BOND);
    }
    if (level_angle == ilevel && force->angle) {
      force->angle->compute(eflag,vflag);
      timer->stamp(Timer::BOND);
    }
    if (level_dihedral == ilevel && force->dihedral) {
      force->dihedral->compute(eflag,vflag);
      timer->stamp(Timer::BOND);
    }
    if (level_improper == ilevel && force->improper) {
      force->improper->compute(eflag,vflag);
      timer->stamp(Timer::BOND);
    }
    if (level_kspace == ilevel && kspace_compute_flag) {
      force->kspace->compute(eflag,vflag);
      timer->stamp(Timer::KSPACE);
    }

    // reduce forces from per-thread arrays, if needed
    if (!fix->get_reduced()) {
      const int nall = atom->nlocal + atom->nghost;
      const int nthreads = comm->nthreads;
#if defined(_OPENMP)
#pragma omp parallel default(none)
#endif
      {
#if defined(_OPENMP)
	int tid = omp_get_thread_num();
#else
	int tid = 0;
#endif
	data_reduce_thr(atom->f[0], nall, nthreads, 3, tid);
      }
      fix->did_reduce();
    }

    if (modify->n_pre_reverse) {
      modify->pre_reverse(eflag,vflag);
      timer->stamp(Timer::MODIFY);
    }
    if (newton[ilevel]) {
      comm->reverse_comm();
      timer->stamp(Timer::COMM);
    }
    timer->stamp();
    if (modify->n_post_force_respa)
      modify->post_force_respa(vflag,ilevel,iloop);
    modify->final_integrate_respa(ilevel,iloop);
    timer->stamp(Timer::MODIFY);
  }

  copy_f_flevel(ilevel);
}