bool CSE_ALifeDynamicObject::synchronize_location	()
{
	if (!ai().level_graph().valid_vertex_position(o_Position) || ai().level_graph().inside(ai().level_graph().vertex(m_tNodeID),o_Position))
		return					(true);

	u32 const new_vertex_id		= ai().level_graph().vertex(m_tNodeID,o_Position);
	if (!m_bOnline && !ai().level_graph().inside(new_vertex_id, o_Position))
		return					(true);

	m_tNodeID					= new_vertex_id;
	GameGraph::_GRAPH_ID		tGraphID = ai().cross_table().vertex(m_tNodeID).game_vertex_id();
	if (tGraphID != m_tGraphID) {
		if (!m_bOnline) {
			Fvector				position = o_Position;
			u32					level_vertex_id = m_tNodeID;
			alife().graph().change	(this,m_tGraphID,tGraphID);
			if (ai().level_graph().inside(ai().level_graph().vertex(level_vertex_id),position)) {
				level_vertex_id	= m_tNodeID;
				o_Position		= position;
			}
		}
		else {
			VERIFY				(ai().game_graph().vertex(tGraphID)->level_id() == alife().graph().level().level_id());
			m_tGraphID			= tGraphID;
		}
	}

	m_fDistance					= ai().cross_table().vertex(m_tNodeID).distance();

	return						(true);
}
void CSE_ALifeOnlineOfflineGroup::unregister_member						(ALife::_OBJECT_ID member_id)
{
	CALifeGraphRegistry			&graph = alife().graph();
	CALifeLevelRegistry			&level = graph.level();

	MEMBERS::iterator			I = m_members.find(member_id);
	VERIFY						(I != m_members.end());
	VERIFY						((*I).second->m_group_id == ID);
	(*I).second->m_group_id		= 0xffff;

	graph.update				((*I).second);
	alife().scheduled().add		((*I).second);

	m_members.erase				(I);

	if (m_members.empty()) {
		if (!m_bOnline) {
			graph.remove		(this,m_tGraphID);
		}
		else {
			if (ID_Parent == 0xffff)
				level.remove	(this);
		}

		m_flags.set				(flUsedAI_Locations,FALSE);
	}
}
void CSE_ALifeOnlineOfflineGroup::try_switch_offline	()
{
	if (m_members.empty())
		return;

	if (!can_switch_offline())
		return;
	
	if (!can_switch_online()) {
		alife().switch_offline	(this);
		return;
	}
	
	MEMBERS::iterator			I = m_members.begin();
	MEMBERS::iterator			E = m_members.end();
	for ( ; I != E; ++I) {
		VERIFY3					((*I).second->g_Alive(),"Incorrect situation : some of the OnlineOffline group members is dead",(*I).second->name_replace());
		VERIFY3					((*I).second->can_switch_offline(),"Incorrect situation : some of the OnlineOffline group members cannot be switched online due to their personal properties",(*I).second->name_replace());
		VERIFY3					((*I).second->can_switch_online(),"Incorrect situation : some of the OnlineOffline group members cannot be switched online due to their personal properties",(*I).second->name_replace());
		
		if (alife().graph().actor()->o_Position.distance_to((*I).second->o_Position) <= alife().offline_distance())
			return;
	}

	alife().switch_offline		(this);
}
void CSE_ALifeOnlineOfflineGroup::register_member						(ALife::_OBJECT_ID member_id)
{
	VERIFY						(m_members.find(member_id) == m_members.end());
	CSE_ALifeDynamicObject		*object = ai().alife().objects().object(member_id);
	CSE_ALifeHumanStalker		*stalker = smart_cast<CSE_ALifeHumanStalker*>(object);
	VERIFY						(stalker);
	VERIFY						(stalker->g_Alive());

	VERIFY						((stalker->m_group_id == 0xffff) || (stalker->m_group_id == ID));
	stalker->m_group_id			= ID;

	bool						empty = m_members.empty();
	m_members.insert			(std::make_pair(member_id,stalker));
	if (empty) {
		o_Position				= stalker->o_Position;
		m_tNodeID				= stalker->m_tNodeID;
		m_tGraphID				= stalker->m_tGraphID;

		m_flags.set				(flUsedAI_Locations,TRUE);

		alife().graph().update	(this);
	}

	if (!object->m_bOnline) {
		alife().graph().remove			(object,object->m_tGraphID);
		alife().scheduled().remove		(object);
	}
	else {
		VERIFY							(object->ID_Parent == 0xffff);
		alife().graph().level().remove	(object);
	}
}
void CSE_ALifeDynamicObject::add_offline			(const xr_vector<ALife::_OBJECT_ID> &saved_children, const bool &update_registries)
{
	if (!update_registries)
		return;

	alife().scheduled().add		(this);
	alife().graph().add			(this,m_tGraphID,false);
}
void CSE_ALifeDynamicObject::add_online				(const bool &update_registries)
{
	if (!update_registries)
		return;

	alife().scheduled().remove	(this);
	alife().graph().remove		(this,m_tGraphID,false);
}
void CSE_ALifeOnlineOfflineGroup::switch_online			()
{
	R_ASSERT					(!m_bOnline);
	m_bOnline					= true;

	MEMBERS::iterator			I = m_members.begin();
	MEMBERS::iterator			E = m_members.end();
	for ( ; I != E; ++I)
		alife().add_online		((*I).second, false);

	alife().scheduled().remove	(this);
	alife().graph().remove		(this,m_tGraphID,false);
}
void CSE_ALifeDynamicObject::try_switch_online		()
{
	CSE_ALifeSchedulable						*schedulable = smart_cast<CSE_ALifeSchedulable*>(this);
	// checking if the abstract monster has just died
	if (schedulable) {
		if (!schedulable->need_update(this)) {
			if (alife().scheduled().object(ID,true))
				alife().scheduled().remove	(this);
		}
		else
			if (!alife().scheduled().object(ID,true))
				alife().scheduled().add		(this);
	}

	if (!can_switch_online()) {
		on_failed_switch_online();
		return;
	}
	
	if (!can_switch_offline()) {
		alife().switch_online	(this);
		return;
	}

	if (alife().graph().actor()->o_Position.distance_to(o_Position) > alife().online_distance()) {
		on_failed_switch_online();
		return;
	}

	alife().switch_online		(this);
}
void CSE_ALifeDynamicObject::try_switch_offline		()
{
	if (!can_switch_offline())
		return;
	
	if (!can_switch_online()) {
		alife().switch_offline	(this);
		return;
	}

	if (alife().graph().actor()->o_Position.distance_to(o_Position) <= alife().offline_distance())
		return;

	alife().switch_offline		(this);
}
bool CSE_ALifeMonsterAbstract::redundant				() const
{
	if (g_Alive())
		return					(false);

	if (m_bOnline)
		return					(false);

	if (m_story_id != INVALID_STORY_ID)
		return					(false);

	if (!m_game_death_time)
		return					(false);

	ALife::_TIME_ID				current_time = alife().time_manager().game_time();
	VERIFY2						(
		m_game_death_time <= current_time,
		make_string(
			"incorrect death time for monster %s[death time = %I64d][current time = %I64d]",
			name_replace(),
			m_game_death_time,
			current_time
		)
	);
	if ((m_game_death_time + m_stay_after_death_time_interval) > current_time)
		return					(false);

	return						(true);
}
void CSE_ALifeMonsterAbstract::vfCheckForPopulationChanges	()
{
	CSE_ALifeGroupAbstract		*l_tpALifeGroupAbstract = smart_cast<CSE_ALifeGroupAbstract*>(this);
	if (!l_tpALifeGroupAbstract || !bfActive() || m_bOnline)
		return;

	ai().ef_storage().alife_evaluation(true);
	ALife::_TIME_ID				l_tTimeID = ai().alife().time_manager().game_time();
	if (l_tTimeID >= l_tpALifeGroupAbstract->m_tNextBirthTime) {
		ai().ef_storage().alife().member() = this;
		l_tpALifeGroupAbstract->m_tNextBirthTime = l_tTimeID + ALife::_TIME_ID(ai().ef_storage().m_pfBirthSpeed->ffGetValue()*24*60*60*1000);
		if (randF(100) < ai().ef_storage().m_pfBirthProbability->ffGetValue()) {
			u32					l_dwBornCount = iFloor(float(l_tpALifeGroupAbstract->m_wCount)*randF(.5f,1.5f)*ai().ef_storage().m_pfBirthPercentage->ffGetValue()/100.f + .5f);
			if (l_dwBornCount) {
				l_tpALifeGroupAbstract->m_tpMembers.resize(l_tpALifeGroupAbstract->m_wCount + l_dwBornCount);
				ALife::OBJECT_IT	I = l_tpALifeGroupAbstract->m_tpMembers.begin() + l_tpALifeGroupAbstract->m_wCount;
				ALife::OBJECT_IT	E = l_tpALifeGroupAbstract->m_tpMembers.end();
				for ( ; I != E; ++I) {
					CSE_Abstract		*l_tpAbstract = alife().create	(l_tpALifeGroupAbstract,this);
					*I					= l_tpAbstract->ID;
				}
				l_tpALifeGroupAbstract->m_wCount = l_tpALifeGroupAbstract->m_wCount + u16(l_dwBornCount);
			}
		}
	}
}
void CSE_ALifeDynamicObject::switch_offline			()
{
	R_ASSERT					(m_bOnline);
	m_bOnline					= false;
	alife().remove_online		(this);

	clear_client_data();
}
void CSE_ALifeDynamicObject::on_register			()
{
	CSE_ALifeObject		*object = this;
	while (object->ID_Parent != ALife::_OBJECT_ID(-1)) {
		object			= ai().alife().objects().object(object->ID_Parent);
		VERIFY			(object);
	}

	if (!alife().graph().level().object(object->ID,true))
		clear_client_data();
}
void CSE_ALifeOnlineOfflineGroup::switch_offline		()
{
	R_ASSERT					(m_bOnline);
	m_bOnline					= false;

	if (!m_members.empty()) {
		MEMBER					*member = (*m_members.begin()).second;
		o_Position				= member->o_Position;
		m_tNodeID				= member->m_tNodeID;
		m_tGraphID				= member->m_tGraphID;
		m_fDistance				= member->m_fDistance;
	}

	MEMBERS::iterator			I = m_members.begin();
	MEMBERS::iterator			E = m_members.end();
	for ( ; I != E; ++I)
		alife().remove_online	((*I).second,false);

	alife().scheduled().add		(this);
	alife().graph().add			(this,m_tGraphID,false);
}
void CSE_ALifeDynamicObject::switch_offline			()
{
	R_ASSERT					(m_bOnline);
	m_bOnline					= false;
	alife().remove_online		(this);
#ifdef DEBUG
	if (!client_data.empty())
		Msg						("CSE_ALifeDynamicObject::switch_offline: client_data is cleared for [%d][%s]",ID,name_replace());
#endif // DEBUG
	if (!keep_saved_data_anyway())
		client_data.clear		();
}
void CSE_InventoryBox::add_offline	(const xr_vector<ALife::_OBJECT_ID> &saved_children, const bool &update_registries)
{
	CSE_ALifeDynamicObjectVisual		*object = (this);

	for (u32 i=0, n=saved_children.size(); i<n; ++i) {
		CSE_ALifeDynamicObject	*child = smart_cast<CSE_ALifeDynamicObject*>(ai().alife().objects().object(saved_children[i],true));
		R_ASSERT				(child);
		child->m_bOnline		= false;

		CSE_ALifeInventoryItem	*inventory_item = smart_cast<CSE_ALifeInventoryItem*>(child);
		VERIFY2					(inventory_item,"Non inventory item object has parent?!");
#ifdef DEBUG
//		if (psAI_Flags.test(aiALife))
//			Msg					("[LSS] Destroying item [%s][%s][%d]",inventory_item->base()->name_replace(),*inventory_item->base()->s_name,inventory_item->base()->ID);
		Msg						(
			"[LSS][%d] Going offline [%d][%s][%d] with parent [%d][%s] on '%s'",
			Device.dwFrame,
			Device.dwTimeGlobal,
			inventory_item->base()->name_replace(),
			inventory_item->base()->ID,
			ID,
			name_replace(),
			"*SERVER*"
		);
#endif
		
		ALife::_OBJECT_ID				item_id = inventory_item->base()->ID;
		inventory_item->base()->ID		= object->alife().server().PerformIDgen(item_id);

		if (!child->can_save()) {
			object->alife().release		(child);
			--i;
			--n;
			continue;
		}

#ifdef DEBUG
		if (!client_data.empty())
			Msg							("CSE_InventoryBox::add_offline: client_data is cleared for [%d][%s]",ID,name_replace());
#endif // DEBUG
		if (!child->keep_saved_data_anyway())
			child->client_data.clear		();
		object->alife().graph().add		(child,child->m_tGraphID,false);
//		object->alife().graph().attach	(*object,inventory_item,child->m_tGraphID,true);
		alife().graph().remove			(child,child->m_tGraphID);
		children.push_back				(child->ID);
		child->ID_Parent				= ID;
	}


	CSE_ALifeDynamicObjectVisual::add_offline(saved_children, update_registries);
}
void CSE_ALifeDynamicObject::try_switch_online		()
{
	CSE_ALifeSchedulable						*schedulable = smart_cast<CSE_ALifeSchedulable*>(this);
	// checking if the abstract monster has just died
	if (schedulable) {
		if (!schedulable->need_update(this)) {
			if (alife().scheduled().object(ID,true))
				alife().scheduled().remove	(this);
		}
		else
			if (!alife().scheduled().object(ID,true))
				alife().scheduled().add		(this);
	}

	if (!can_switch_online()) {
#ifdef DEBUG
		if (!client_data.empty())
			Msg					("CSE_ALifeDynamicObject::try_switch_online: client_data is cleared for [%d][%s]",ID,name_replace());
#endif // DEBUG
		if (!keep_saved_data_anyway())
			client_data.clear	();
		return;
	}
	
	if (!can_switch_offline()) {
		alife().switch_online	(this);
		return;
	}

	if (alife().graph().actor()->o_Position.distance_to(o_Position) > alife().online_distance()) {
#ifdef DEBUG
		if (!client_data.empty())
			Msg					("CSE_ALifeDynamicObject::try_switch_online2: client_data is cleared for [%d][%s]",ID,name_replace());
#endif // DEBUG
		if (!keep_saved_data_anyway())
			client_data.clear	();
		return;
	}

	alife().switch_online		(this);
}
bool CSE_ALifeOnlineOfflineGroup::synchronize_location	()
{
	if (m_members.empty())
		return					(true);

	MEMBERS::iterator			I = m_members.begin();
	MEMBERS::iterator			E = m_members.end();
	for ( ; I != E; ++I)
		(*I).second->synchronize_location	();

	MEMBER						&member = *(*m_members.begin()).second;
	o_Position					= member.o_Position;
	m_tNodeID					= member.m_tNodeID;

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

	m_fDistance					= member.m_fDistance;
	return						(true);
}
void CSE_ALifeAnomalousZone::spawn_artefacts				()
{
	VERIFY2					(!m_bOnline,"Cannot spawn artefacts in online!");

	float					m_min_start_power	= pSettings->r_float(name(),"min_start_power");
	float					m_max_start_power	= pSettings->r_float(name(),"max_start_power");
	u32						m_min_artefact_count= pSettings->r_u32	(name(),"min_artefact_count");;
	u32						m_max_artefact_count= pSettings->r_u32	(name(),"max_artefact_count");;
    u32						m_artefact_count;

	if (m_min_artefact_count == m_max_artefact_count)
		m_artefact_count	= m_min_artefact_count;
	else
		m_artefact_count	= randI(m_min_artefact_count,m_max_artefact_count);

	if (m_min_start_power == m_max_start_power)
		m_maxPower			= m_min_start_power;
	else
		m_maxPower			= randF(m_min_start_power,m_max_start_power);

	LPCSTR					artefacts = pSettings->r_string(name(),"artefacts");
	u32						n = _GetItemCount(artefacts);
	VERIFY2					(!(n % 2),"Invalid parameters count in line artefacts for anomalous zone");
	n						>>= 1;
	
	typedef std::pair<shared_str,float>	ARTEFACT_PAIR;

	string256				temp0, temp1;
	ARTEFACT_PAIR			*m_weights = (ARTEFACT_PAIR*)_alloca(n*sizeof(ARTEFACT_PAIR));
	ARTEFACT_PAIR			*I = m_weights;
	ARTEFACT_PAIR			*E = m_weights + n;
	for (u32 i = 0; I != E; ++I, ++i) {
		_GetItem			(artefacts,2*i,temp0);
		_GetItem			(artefacts,2*i + 1,temp1);
		new					(I) ARTEFACT_PAIR(temp0,(float)atof(temp1));
	}

	for (u32 ii=0; ii<m_artefact_count; ++ii) {
		float fProbability		= randF(1.f);
		float fSum				= 0.f;
		for (u16 p=0; p<n; ++p) {
			fSum			+= m_weights[p].second;
			if (fSum > fProbability)
				break;
		}
		if (p < n) {
			CSE_Abstract		*l_tpSE_Abstract = alife().spawn_item(*m_weights[p].first,position(),m_tNodeID,m_tGraphID,0xffff);
			R_ASSERT3			(l_tpSE_Abstract,"Can't spawn artefact ",*m_weights[p].first);
			CSE_ALifeDynamicObject	*i = smart_cast<CSE_ALifeDynamicObject*>(l_tpSE_Abstract);
			R_ASSERT2			(i,"Non-ALife object in the 'game.spawn'");

			i->m_tSpawnID		= m_tSpawnID;
			i->m_bALifeControl	= true;
			ai().alife().spawns().assign_artefact_position(this,i);

			Fvector				t = i->o_Position	;
			u32					p = i->m_tNodeID	;
			float				q = i->m_fDistance	;
			alife().graph().change(i,m_tGraphID,i->m_tGraphID);
			i->o_Position		= t;
			i->m_tNodeID		= p;
			i->m_fDistance		= q;

			CSE_ALifeItemArtefact *l_tpALifeItemArtefact = smart_cast<CSE_ALifeItemArtefact*>(i);
			R_ASSERT2		(l_tpALifeItemArtefact,"Anomalous zone can't generate non-artefact objects since they don't have an 'anomaly property'!");

			l_tpALifeItemArtefact->m_fAnomalyValue = m_maxPower*(1.f - i->o_Position.distance_to(o_Position)/m_offline_interactive_radius);
		}
	}

	for (I = m_weights; I != E; ++I)
		I->~ARTEFACT_PAIR		();
}
void CSE_ALifeDynamicObject::switch_online			()
{
	R_ASSERT					(!m_bOnline);
	m_bOnline					= true;
	alife().add_online			(this);
}