void CSE_ALifeGroupAbstract::switch_online	()
{
	CSE_ALifeDynamicObject		*object = smart_cast<CSE_ALifeDynamicObject*>(this);
	VERIFY						(object);

	R_ASSERT					(!object->m_bOnline);
	object->m_bOnline			= true;

	ALife::OBJECT_IT			I = m_tpMembers.begin(), B = I;
	ALife::OBJECT_IT			E = m_tpMembers.end();
	u32							N = (u32)(E - I);
	for ( ; I != E; ++I) {
		CSE_ALifeDynamicObject	*J = ai().alife().objects().object(*I);
		if (m_bCreateSpawnPositions) {
			J->o_Position		= object->o_Position;
			J->m_tNodeID		= object->m_tNodeID;
			CSE_ALifeMonsterAbstract	*l_tpALifeMonsterAbstract = smart_cast<CSE_ALifeMonsterAbstract*>(J);
			if (l_tpALifeMonsterAbstract)
				l_tpALifeMonsterAbstract->o_torso.yaw = angle_normalize_signed((I - B)/N*PI_MUL_2);
		}
		object->alife().add_online	(J, false);
	}
	m_bCreateSpawnPositions		= false;
	object->alife().scheduled().remove	(object);
	object->alife().graph().remove		(object,object->m_tGraphID,false);
}
void CSE_ALifeGroupAbstract::switch_offline	()
{
	CSE_ALifeDynamicObject		*object = smart_cast<CSE_ALifeDynamicObject*>(base());
	VERIFY						(object);

	R_ASSERT					(object->m_bOnline);
	object->m_bOnline			= false;

	ALife::OBJECT_IT			I = m_tpMembers.begin();
	ALife::OBJECT_IT			E = m_tpMembers.end();
	if (I != E) {
		CSE_ALifeMonsterAbstract		*tpGroupMember	= smart_cast<CSE_ALifeMonsterAbstract*>(ai().alife().objects().object(*I));
		CSE_ALifeMonsterAbstract		*tpGroup		= smart_cast<CSE_ALifeMonsterAbstract*>(this);
		if (tpGroupMember && tpGroup) {
			tpGroup->m_fCurSpeed		= tpGroup->m_fCurrentLevelGoingSpeed;
			tpGroup->o_Position			= tpGroupMember->o_Position;
			u32	dwNodeID				= tpGroup->m_tNodeID;
			tpGroup->m_tGraphID			= ai().cross_table().vertex(dwNodeID).game_vertex_id();
			tpGroup->m_fDistanceToPoint = ai().cross_table().vertex(dwNodeID).distance();
			tpGroup->m_tNextGraphID		= tpGroup->m_tGraphID;
			u16	wNeighbourCount			= ai().game_graph().vertex(tpGroup->m_tGraphID)->edge_count();
			CGameGraph::const_iterator	i,e;
			ai().game_graph().begin		(tpGroup->m_tGraphID,i,e);
			tpGroup->m_tPrevGraphID		= (*(i + object->randI(0,wNeighbourCount))).vertex_id();
		}
		object->alife().remove_online	(tpGroupMember,false);
		++I;
	}
	for ( ; I != E; ++I)
		object->alife().remove_online	(ai().alife().objects().object(*I),false);
	object->alife().scheduled().add		(object);
	object->alife().graph().add			(object,object->m_tGraphID,false);
}
bool CSE_ALifeGroupAbstract::synchronize_location	()
{
	if (m_tpMembers.empty())
		return					(true);

	CSE_ALifeDynamicObject		*object = smart_cast<CSE_ALifeDynamicObject*>(base());
	VERIFY						(object);
	
	ALife::OBJECT_VECTOR::iterator	I = m_tpMembers.begin();
	ALife::OBJECT_VECTOR::iterator	E = m_tpMembers.end();
	for ( ; I != E; ++I)
		ai().alife().objects().object(*I)->synchronize_location	();

	CSE_ALifeDynamicObject		&member = *ai().alife().objects().object(*I);
	object->o_Position			= member.o_Position;
	object->m_tNodeID			= member.m_tNodeID;

	if (object->m_tGraphID != member.m_tGraphID) {
		if (!object->m_bOnline)
			object->alife().graph().change	(object,object->m_tGraphID,member.m_tGraphID);
		else
			object->m_tGraphID	= member.m_tGraphID;
	}

	object->m_fDistance			= member.m_fDistance;
	return						(true);
}
void CSE_ALifeGroupAbstract::try_switch_offline		()
{
	// checking if group is not empty
	if (m_tpMembers.empty())
		return;

	// so, we have a group of objects
	// therefore check all the group members if they are ready to switch offline

	CSE_ALifeDynamicObject				*I = smart_cast<CSE_ALifeDynamicObject*>(base());
	VERIFY								(I);
	
	// iterating on group members
	u32 i = 0;
	u32 N = (u32)m_tpMembers.size();
	for (; i<N; ++i) {
		// casting group member to the abstract monster to get access to the Health property
		CSE_ALifeMonsterAbstract		*tpGroupMember = smart_cast<CSE_ALifeMonsterAbstract*>(ai().alife().objects().object(m_tpMembers[i]));
		if (!tpGroupMember)
			continue;
			
		// check if monster is not dead
		if (tpGroupMember->g_Alive()) {
			// so, monster is not dead
			// checking if the object is _not_ ready to switch offline
			if (!tpGroupMember->can_switch_offline())
				continue;
			
			if (!tpGroupMember->can_switch_online())
				// so, it is not ready, breaking a cycle, because we can't 
				// switch group offline since not all the group members are ready
				// to switch offline
				break;
			
			if (I->alife().graph().actor()->o_Position.distance_to(tpGroupMember->o_Position) <= I->alife().offline_distance())
				// so, it is not ready, breaking a cycle, because we can't 
				// switch group offline since not all the group members are ready
				// to switch offline
				break;

			continue;
		}

		// detach object from the group
		tpGroupMember->fHealth				= 0.f;
		tpGroupMember->m_bDirectControl		= true;
		m_tpMembers.erase					(m_tpMembers.begin() + i);
		tpGroupMember->m_bOnline			= false;
		CSE_ALifeInventoryItem				*item = smart_cast<CSE_ALifeInventoryItem*>(tpGroupMember);
		if (item && item->attached()) {
			CSE_ALifeDynamicObject			*object = ai().alife().objects().object(tpGroupMember->ID_Parent,true);
			if (object)
				object->detach				(item);
		}
		// store the __new separate object into the registries
		I->alife().register_object			(tpGroupMember);

		// and remove it from the graph point but do not remove it from the current level map
		CSE_ALifeInventoryItem				*l_tpALifeInventoryItem = smart_cast<CSE_ALifeInventoryItem*>(tpGroupMember);
		if (!l_tpALifeInventoryItem || !l_tpALifeInventoryItem->attached())
			I->alife().graph().remove		(tpGroupMember,tpGroupMember->m_tGraphID,false);

		tpGroupMember->m_bOnline			= true;
		--m_wCount;
		--i;
		--N;
	}

	// checking if group is not empty
	if (m_tpMembers.empty())
		return;

	if (!I->can_switch_offline())
		return;
	
	if (I->can_switch_online() || (i == N))
		I->alife().switch_offline			(I);
}