<commit_before>#include "SmartObject.h"

namespace DEM::Game
{
RTTI_CLASS_IMPL(DEM::Game::CSmartObject, Resources::CResourceObject);

CSmartObject::CSmartObject(CStrID ID, CStrID DefaultState, std::string_view ScriptSource)
	: _ID(ID)
	, _DefaultState(DefaultState)
	, _ScriptSource(ScriptSource)
{
}
//---------------------------------------------------------------------

bool CSmartObject::AddState(CStrID ID, CTimelineTask&& TimelineTask/*, state logic object ptr (optional)*/)
{
	// Check if state with the same ID exists
	auto It = std::lower_bound(_States.begin(), _States.end(), ID, [](const auto& Elm, CStrID Value) { return Elm.ID < Value; });
	if (It != _States.end() && (*It).ID == ID) return false;

	// Insert sorted by ID
	_States.insert(It, { ID, std::move(TimelineTask) });

	return true;
}
//---------------------------------------------------------------------

bool CSmartObject::AddTransition(CStrID FromID, CStrID ToID, CTimelineTask&& TimelineTask)
{
	// Find source state, it must exist
	auto It = std::lower_bound(_States.begin(), _States.end(), FromID, [](const auto& Elm, CStrID Value) { return Elm.ID < Value; });
	if (It == _States.end() || (*It).ID != FromID) return false;

	// FIXME: for now don't check dest state existence due to loading order in CSmartObjectLoader

	// Check if this transition already exists
	auto It2 = std::lower_bound(It->Transitions.begin(), It->Transitions.end(), ToID, [](const auto& Elm, CStrID Value) { return Elm.TargetStateID < Value; });
	if (It2 != It->Transitions.end() && (*It2).TargetStateID == ToID) return false;

	// Insert sorted by ID
	It->Transitions.insert(It2, { ToID, std::move(TimelineTask) });

	return true;
}
//---------------------------------------------------------------------

bool CSmartObject::AddInteraction(CStrID ID)
{
	// Check if this interaction exists
	auto It = std::lower_bound(_Interactions.begin(), _Interactions.end(), ID, [](const auto& Elm, CStrID Value) { return Elm.first < Value; });
	if (It != _Interactions.end() && (*It).first == ID) return false;

	// Insert sorted by ID
	_Interactions.insert(It, { ID, sol::function() });

	return true;
}
//---------------------------------------------------------------------

const CSmartObjectStateInfo* CSmartObject::FindState(CStrID ID) const
{
	// Find source state, it must exist
	auto It = std::lower_bound(_States.begin(), _States.end(), ID, [](const auto& Elm, CStrID Value) { return Elm.ID < Value; });
	return (It == _States.end() || (*It).ID != ID) ? nullptr : &(*It);<commit_msg>SO deny empty ID for states, transitions and interactions<commit_after>#include "SmartObject.h"

namespace DEM::Game
{
RTTI_CLASS_IMPL(DEM::Game::CSmartObject, Resources::CResourceObject);

CSmartObject::CSmartObject(CStrID ID, CStrID DefaultState, std::string_view ScriptSource)
	: _ID(ID)
	, _DefaultState(DefaultState)
	, _ScriptSource(ScriptSource)
{
}
//---------------------------------------------------------------------

bool CSmartObject::AddState(CStrID ID, CTimelineTask&& TimelineTask/*, state logic object ptr (optional)*/)
{
	if (!ID) return false;

	// Check if state with the same ID exists
	auto It = std::lower_bound(_States.begin(), _States.end(), ID, [](const auto& Elm, CStrID Value) { return Elm.ID < Value; });
	if (It != _States.end() && (*It).ID == ID) return false;

	// Insert sorted by ID
	_States.insert(It, { ID, std::move(TimelineTask) });

	return true;
}
//---------------------------------------------------------------------

bool CSmartObject::AddTransition(CStrID FromID, CStrID ToID, CTimelineTask&& TimelineTask)
{
	if (!FromID || !ToID) return false;

	// Find source state, it must exist
	auto It = std::lower_bound(_States.begin(), _States.end(), FromID, [](const auto& Elm, CStrID Value) { return Elm.ID < Value; });
	if (It == _States.end() || (*It).ID != FromID) return false;

	// FIXME: for now don't check dest state existence due to loading order in CSmartObjectLoader

	// Check if this transition already exists
	auto It2 = std::lower_bound(It->Transitions.begin(), It->Transitions.end(), ToID, [](const auto& Elm, CStrID Value) { return Elm.TargetStateID < Value; });
	if (It2 != It->Transitions.end() && (*It2).TargetStateID == ToID) return false;

	// Insert sorted by ID
	It->Transitions.insert(It2, { ToID, std::move(TimelineTask) });

	return true;
}
//---------------------------------------------------------------------

bool CSmartObject::AddInteraction(CStrID ID)
{
	if (!ID) return false;

	// Check if this interaction exists
	auto It = std::lower_bound(_Interactions.begin(), _Interactions.end(), ID, [](const auto& Elm, CStrID Value) { return Elm.first < Value; });
	if (It != _Interactions.end() && (*It).first == ID) return false;

	// Insert sorted by ID
	_Interactions.insert(It, { ID, sol::function() });

	return true;
}
//---------------------------------------------------------------------

const CSmartObjectStateInfo* CSmartObject::FindState(CStrID ID) const
{
	// Find source state, it must exist
	auto It = std::lower_bound(_States.begin(), _States.end(), ID, [](const auto& Elm, CStrID Value) { return Elm.ID < Value; });
	return (It == _States.end() || (*It).ID != ID) ? nullptr : &(*It);
{'fraction_non_alphanumeric': 0.2511520737327189, 'fraction_numerical': 0.0015360983102918587, 'mean_word_length': 2.8671121009651075, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2221608', 'n_tokens_mistral': 1615, 'n_tokens_neox': 1581, 'n_words': 444}